1use chrono::{NaiveDateTime, OutOfRangeError};
54use mysql::Error as MySqlError;
55use thiserror::Error;
56
57#[derive(Debug, Error)]
58pub enum SqlInterfaceError {
60 #[error(
62 "[{location}] Could not get a connection from the pool. The database may be down or the pool exhausted."
63 )]
64 ConnectionFromPool {
65 location: String,
66
67 #[source]
68 source: mysql::Error,
69 },
70
71 #[error("[{0}] The database wait_timeout ({1}s) is less than the required minimum ({2}s).")]
73 WaitTimeoutTooLow(String, u64, u64),
74
75 #[error("[{0}] Could not parse wait_timeout value from the database: {0}")]
77 WaitTimeoutInvalid(String, i64),
78
79 #[error("[{location}] Could not establish database connection pool with {url}")]
81 ConnectionPoolFailure {
82 location: String,
83 url: String,
84
85 #[source]
86 source: mysql::Error,
87 },
88
89 #[error("[{0}] Required tables ({1}) are not existing in the database ({2}).")]
91 RequiredTablesNotExisting(String, String, String),
92
93 #[error("[{0}] Error when reading tables from the database, expected more than one result.")]
95 ShowTablesWithoutProperResult(String),
96
97 #[error("[{0}] Could not get a single string from the database using query: {1} with major={2} and minor={3} and build={4}")]
99 SingleVersionRequestFailure(String, String, i32, i32, i32),
100
101 #[error("[{0}] Database responded with empty response to query: {1} with major={2} and minor={3} and build={4}")]
103 SingleVersionRequestNotListed(String, String, i32, i32, i32),
104
105 #[error("[{0}] Database responded with multiple responses to request to query: {1} with major={2} and minor={3} and build={4}")]
107 SingleVersionRequestMultipleResults(String, String, i32, i32, i32),
108
109 #[error("[{location}] Could not get a string array from the database using query: {query}")]
111 SingleStringRequestFailure {
112 location: String,
113 query: String,
114
115 #[source]
116 source: mysql::Error,
117 },
118
119 #[error("[{0}] database responded with multiple responses to request to query: {1}")]
121 SingleStringRequestNoSingleResponse(String, String),
122
123 #[error("[{0}] database responded with empty response to request to query: {1}")]
125 SingleStringRequestEmptyResponse(String, String),
126
127 #[error("[{0}] Database responded with empty response to query: {1}")]
129 MultipleStringRequestEmptyResponse(String, String),
130
131 #[error("[{0}] Database responded with single row response to query: {1}")]
133 MultipleStringRequestSingleResponse(String, String),
134
135 #[error("[{0}] Could not get a single integer from the database using query: {1}")]
137 SingleIntegerRequestFailure(String, String),
138
139 #[error("[{0}] Database responded with multiple responses to request to query: {1}")]
141 SingleIntegerRequestNoSingleResponse(String, String),
142
143 #[error("[{0}] Could not get single float value from the database using query: {1}")]
145 SingleFloatRequestFailure(String, String),
146
147 #[error("[{0}] Database responded with empty response to query: {1}")]
149 SingleFloatRequestEmptyResponse(String, String),
150
151 #[error("[{0}] Database responded with multiple responses to request to query: {1}")]
153 SingleFloatRequestNoSingleResponse(String, String),
154
155 #[error(
157 "[{location}] Could not get single heating stats entry from database using query: {query}"
158 )]
159 SingleHeatingStatsRequestFailure {
160 location: String,
161 query: String,
162 #[source]
163 source: MySqlError,
164 },
165
166 #[error("[{0}] Database responded with empty response to query: {1}")]
168 SingleHeatingStatsRequestEmptyResponse(String, String),
169
170 #[error("[{0}] Database responded with multiple responses to request to query: {1}")]
172 SingleHeatingStatsRequestNoSingleResponse(String, String),
173
174 #[error(
176 "[{location}] Could not get feed pattern header from the database with query: {query} with profile_id={profile_id}"
177 )]
178 SingleFeedpatternRequestFailure {
179 location: String,
180 query: String,
181 profile_id: i32,
182
183 #[source]
184 source: MySqlError,
185 },
186
187 #[error("[{0}] Database responded with empty response to query: {1} with profile_id={2}")]
189 SingleFeedpatternRequestEmptyResponse(String, String, i32),
190
191 #[error("[{0}] Database responded with multiple responses to request to query: {1} with profile_id={2}")]
193 SingleFeedpatternRequestNoSingleResponse(String, String, i32),
194
195 #[error("[{location}] Could not get schedule entries from database using query: {query}")]
197 ScheduleRequestFailure {
198 location: String,
199 query: String,
200 #[source]
201 source: MySqlError,
202 },
203
204 #[error("[{location}] Could not process the results obtained from database.")]
206 ScheduleProcessingFailure {
207 location: String,
208
209 #[source]
210 source: Box<SqlInterfaceError>,
211 },
212
213 #[error("[{0}] Error in conversion of database result to schedule entry ({1}).")]
215 ScheduleTypeConversionFailure(String, String),
216
217 #[error(
219 "[{0}] Schedule of type {1} could not be found in database. Existing entries are: {2}."
220 )]
221 ScheduleTypeNotFound(String, String, String),
222
223 #[error(
225 "[{location}] Error when inserting heating stats into the database using query: {query}"
226 )]
227 InsertHeatingStatsEntryFailure {
228 location: String,
229 query: String,
230 #[source]
231 source: MySqlError,
232 },
233
234 #[error("[{location}] Error occurred when trying to insert a feed event into the database: {source}")]
236 InsertFeedEventFailure {
237 location: String,
238
239 #[source]
240 source: MySqlError,
241 },
242
243 #[error("[{location}] Error occurred when trying to insert a refill event into the database using query: {query}.")]
245 InsertRefillEventFailure {
246 location: String,
247 query: String,
248 #[source]
249 source: MySqlError,
250 },
251
252 #[error(
254 "[{0}] Database contains a Balling dosing event for pump {1} which is in the future: {2}."
255 )]
256 BallingDosingLogEntryInFuture(String, i64, NaiveDateTime),
257
258 #[error("[{location}] Error occurred when trying to calculate dosing duration for pump {pump_id} from two NaiveDateTime timestamps ({current_datetime}, {last_dosing_datetime}): {source}")]
260 BallingDosingTimestampConversionFailure {
261 location: String,
262 pump_id: i64,
263 current_datetime: NaiveDateTime,
264 last_dosing_datetime: NaiveDateTime,
265
266 #[source]
267 source: OutOfRangeError,
268 },
269
270 #[error(
272 "[{location}] Error occurred when trying to insert a Balling dosing event into database."
273 )]
274 InsertBallingEventFailure {
275 location: String,
276 #[source]
277 source: MySqlError,
278 },
279
280 #[error("[{location}] Database request for retrieving the refill {category} of last {interval} failed.")]
282 RefillPastDataRetrievalFailed {
283 location: String,
284 category: String,
285 interval: String,
286 #[source]
287 source: Box<SqlInterfaceError>,
288 },
289
290 #[error(
292 "[{location}] Could not get feed schedule entry from the database using query: {query}"
293 )]
294 FeedScheduleEntryRequestFailure {
295 location: String,
296 query: String,
297
298 #[source]
299 source: MySqlError,
300 },
301
302 #[error("[{location}] Repetition marker is out of range (repeat_daily = {repeat_daily}).")]
304 FeedScheduleEntryRepeatDailyOutOfRange { location: String, repeat_daily: i16 },
305
306 #[error(
308 "[{location}] Error occurred when processing {number_entries} results from the database."
309 )]
310 FeedScheduleEntryTimeStampProcessingFailure {
311 location: String,
312 number_entries: usize,
313 #[source]
314 source: Box<SqlInterfaceError>,
315 },
316
317 #[error("[{location}] Error occurred when trying to calculate update of feed schedule entry.")]
319 FeedScheduleUpdateFailure {
320 location: String,
321 #[source]
322 source: Box<SqlInterfaceError>,
323 },
324
325 #[error(
327 "[{location}] Error occurred when trying to drop a feed schedule entry from the database."
328 )]
329 FeedScheduleDropFailure {
330 location: String,
331 #[source]
332 source: MySqlError,
333 },
334
335 #[error(
337 "[{0}] Database responded with negative value(s) for feed phase/feed pause duration: {1} {2}"
338 )]
339 DatabaseFeedPhaseDurationNegative(String, i16, i16),
340
341 #[error("[{location}] Could not get a single Balling set value from the database using query: {query}")]
343 SingleBallingSetValRequestFailure {
344 location: String,
345 query: String,
346 pump_id: i64,
347
348 #[source]
349 source: MySqlError,
350 },
351
352 #[error("[{0}] Database responded with empty response to query: {1} with pump_id={2}")]
354 SingleBallingSetValEmptyResponse(String, String, i64),
355
356 #[error("[{0}] Database responded with multiple responses to request to query: {1} with pump_id={2}")]
358 SingleBallingSetValNoSingleResponse(String, String, i64),
359
360 #[error("[{location}] Error occurred when inserting data frame into the database using query: {query}")]
362 InsertDataFrameFailure {
363 location: String,
364 query: String,
365
366 #[source]
367 source: MySqlError,
368 },
369
370 #[error("[{0}] Database responded with a timestamp in the future (delta of {1} seconds).")]
372 NegativeTimeDeltaSeconds(String, i64),
373
374 #[error(
376 "[{0}] Database responded with a negative value ({1}) where a positive value was expected."
377 )]
378 NegativeInteger(String, i64),
379
380 #[error("[{location}] Could not get heating set values from the database with query: {query}")]
382 HeatingSetValsRequestFailure {
383 location: String,
384 query: String,
385
386 #[source]
387 source: MySqlError,
388 },
389
390 #[error("[{0}] The database returned more than one heating set value entry with query: {1}")]
392 HeatingSetValsNoSingleResponse(String, String),
393
394 #[error("[{location}] Updating the heating set values failed.")]
396 HeatingSetValsUpdateFailure {
397 location: String,
398
399 #[source]
400 source: Box<SqlInterfaceError>,
401 },
402
403 #[error("[{location}] Updating the ventilation set values failed.")]
405 VentilationSetValsUpdateFailure {
406 location: String,
407
408 #[source]
409 source: Box<SqlInterfaceError>,
410 },
411
412 #[error("[{0}] The heating switch on temperature ({1}) is higher than the heating switch-off temperature ({2}).")]
414 HeatingSetValsInvalid(String, f32, f32),
415
416 #[error(
418 "[{location}] Could not get ventilation set values from the database with query: {query}"
419 )]
420 VentilationSetValsRequestFailure {
421 location: String,
422 query: String,
423
424 #[source]
425 source: MySqlError,
426 },
427
428 #[error(
430 "[{0}] The database returned more than one ventilation set value entry with query: {1}"
431 )]
432 VentilationSetValsNoSingleResponse(String, String),
433
434 #[error("[{0}] The ventilation switch on temperature ({1}) is lower than the ventilation switch-off temperature ({2}).")]
436 VentilationSetValsInvalid(String, f32, f32),
437
438 #[error("[{location}] Could not retrieve database name.")]
440 DatabaseNameRequestFailure {
441 location: String,
442
443 #[source]
444 source: Box<SqlInterfaceError>,
445 },
446
447 #[error("[{location}] Could not retrieve date from the database.")]
449 DateRequestFailure { location: String, query: String },
450
451 #[error("[{location}] Could not retrieve timestamp from database using query: {query}")]
453 TimestampRequestFailure { location: String, query: String },
454
455 #[error("[{location}] Could not retrieve optional timestamp from database.")]
457 OptionalTimestampRequestFailure { location: String, query: String },
458
459 #[error("[{location}] Could not ping database.")]
461 DatabasePingFailure {
462 location: String,
463
464 #[source]
465 source: Box<SqlInterfaceError>,
466 },
467
468 #[error("[{0}] Balling set value table may be inconsistent - it contains {1} NULL values.")]
470 DatabaseBallingSetValTableContainsNull(String, i64),
471
472 #[error("[{0}] Balling set value table may be inconsistent - database responded with negative value(s) ({1}, {2}, {3}).")]
474 DatabaseBallingSetValTableNegativeValue(String, i64, i64, i64),
475
476 #[error(
478 "[{0}] Balling set value table contains too many rows ({1}). Permissible maximum is {2}."
479 )]
480 DatabaseBallingSetValTableContainsTooManyRows(String, u64, u64),
481
482 #[error(
484 "[{0}] Balling dosing log table contains too many rows ({1}). Permissible maximum is {2}."
485 )]
486 DatabaseBallingDosingLogTableContainsTooManyRows(String, u64, u64),
487
488 #[error("[{0}] Schedule table may be inconsistent - it contains {1} NULL values.")]
490 DatabaseScheduleTableContainsNull(String, i64),
491
492 #[error("[{0}] Schedule table may be inconsistent - database responded with negative value(s) ({1}, {2}).")]
494 DatabaseScheduleTableNegativeValue(String, i64, i64),
495
496 #[error("[{0}] Schedule table contains too many rows ({1}). Permissible maximum is {2}.")]
498 DatabaseScheduleTableContainsTooManyRows(String, u64, u64),
499
500 #[error("[{location}] Could not check data table for number of rows.")]
502 DatabaseCheckDataFailure {
503 location: String,
504
505 #[source]
506 source: Box<SqlInterfaceError>,
507 },
508
509 #[error(
511 "[{0}] Data table may be inconsistent - database responded with negative value ({1})."
512 )]
513 DatabaseDataTableNegativeValue(String, i64),
514
515 #[error("[{0}] Data table contains too many rows ({1}). Permissible maximum is {2}.")]
517 DatabaseDataTableContainsTooManyRows(String, u64, u64),
518
519 #[error("[{0}] Heating set value table may be inconsistent - it contains {1} NULL values.")]
521 DatabaseHeatingSetValTableContainsNull(String, i64),
522
523 #[error("[{0}] Heating set value table may be inconsistent - database responded with negative value(s) ({1}, {2}).")]
525 DatabaseHeatingSetValTableNegativeValue(String, i64, i64),
526
527 #[error(
529 "[{0}] Heating set value table contains too many rows ({1}). Permissible maximum is 1."
530 )]
531 DatabaseHeatingSetValTableContainsTooManyRows(String, u64),
532
533 #[error("[{0}] Heating stats table may be inconsistent - it contains {1} NULL values.")]
535 DatabaseHeatingStatsTableContainsNull(String, i64),
536
537 #[error("[{0}] Heating set value table may be inconsistent - database responded with negative value(s) ({1}, {2}).")]
539 DatabaseHeatingStatsTableNegativeValue(String, i64, i64),
540
541 #[error(
543 "[{0}] Heating statistics table contains too many rows ({1}). Permissible maximum is {2}."
544 )]
545 DatabaseHeatingStatsTableContainsTooManyRows(String, u64, u64),
546
547 #[error(
549 "[{0}] Ventilation set value table may be inconsistent - it contains {1} NULL values."
550 )]
551 DatabaseVentilationSetValTableContainsNull(String, i64),
552
553 #[error("[{0}] Ventilation set value table may be inconsistent - database responded with negative value(s) ({1}, {2}).")]
555 DatabaseVentilationSetValTableNegativeValue(String, i64, i64),
556
557 #[error(
559 "[{0}] Ventilation set value table contains too many rows ({1}). Permissible maximum is 1."
560 )]
561 DatabaseVentilationSetValTableContainsTooManyRows(String, u64),
562
563 #[error(
565 "[{0}] Refill table may be inconsistent - database responded with negative value ({1})."
566 )]
567 DatabaseRefillTableNegativeValue(String, i64),
568
569 #[error("[{0}] Refill table contains too many rows ({1}). Permissible maximum is {2}.")]
571 DatabaseRefillTableContainsTooManyRows(String, u64, u64),
572
573 #[error("[{0}] Feed set value table {1} may be inconsistent - it contains {2} NULL values.")]
575 DatabaseFeedSetValTableContainsNull(String, String, i64),
576
577 #[error("[{0}] Feed table {1} contains too many rows ({2}). Permissible maximum is {3}.")]
579 DatabaseFeedTableContainsTooManyRows(String, String, u64, u64),
580
581 #[error(
583 "[{location}] Could not check balling set value table (existence of NULL values and number of rows)."
584 )]
585 DatabaseCheckBallingSetValsFailure {
586 location: String,
587
588 #[source]
589 source: Box<SqlInterfaceError>,
590 },
591
592 #[error("[{location}] Could not check schedule table for NULL values or number of rows.")]
594 DatabaseCheckScheduleFailure {
595 location: String,
596
597 #[source]
598 source: Box<SqlInterfaceError>,
599 },
600
601 #[error(
603 "[{location}] Could not check heating set value table for NULL values or number of rows."
604 )]
605 DatabaseCheckHeatingSetValsFailure {
606 location: String,
607
608 #[source]
609 source: Box<SqlInterfaceError>,
610 },
611
612 #[error("[{location}] Could not check heating stats table for NULL values or number of rows.")]
614 DatabaseCheckHeatingStatsFailure {
615 location: String,
616
617 #[source]
618 source: Box<SqlInterfaceError>,
619 },
620
621 #[error("[{location}] Could not check ventilation set value table for NULL values or number of rows.")]
623 DatabaseCheckVentilationSetValsFailure {
624 location: String,
625
626 #[source]
627 source: Box<SqlInterfaceError>,
628 },
629
630 #[error("[{location}] Could not check refill table for number of rows.")]
632 DatabaseCheckRefillFailure {
633 location: String,
634
635 #[source]
636 source: Box<SqlInterfaceError>,
637 },
638
639 #[error(
641 "[{location}] Could not check feed set value table {table_name} for NULL values or number of rows."
642 )]
643 DatabaseCheckFeedSetValsFailure {
644 location: String,
645 table_name: String,
646 #[source]
647 source: Box<SqlInterfaceError>,
648 },
649
650 #[error("[{0}] Feed set value table {1} may be inconsistent - database responded with negative value(s) ({2}, {3}).")]
652 DatabaseFeedTableNegativeValue(String, String, i64, i64),
653
654 #[cfg(test)]
655 #[error(
656 "[{location}] Error when inserting heating set values into database using query: {query}"
657 )]
658 InsertHeatingSetValuesFailure {
659 location: String,
660 query: String,
661
662 #[source]
663 source: MySqlError,
664 },
665
666 #[cfg(test)]
667 #[error("[{location}] Error when inserting ventilation set values into database using query: {query}")]
668 InsertVentilationSetValuesFailure {
669 location: String,
670 query: String,
671
672 #[source]
673 source: MySqlError,
674 },
675
676 #[cfg(test)]
677 #[error("[{0}] Error occurred when trying to truncate table {1} of database.")]
678 TruncateTableFailure(String, String),
679
680 #[cfg(test)]
681 #[error("[{0}] This is a mock error generated for testing purposes.")]
682 MockError(String),
683
684 #[cfg(test)]
685 #[error(
686 "[{0}] Error when inserting Balling pump configuration into database using query: {1}"
687 )]
688 InsertBallingPumpConfigurationFailure(String, String),
689
690 #[cfg(test)]
691 #[error("[{0}] Database responded with empty response to queries: {1} {2}")]
692 SingleDataFrameRequestEmptyResponse(String, String, String),
693
694 #[cfg(test)]
695 #[error("[{0}] Database responded with multiple responses to queries: {1} {2}")]
696 SingleDataFrameRequestNoSingleResponse(String, String, String),
697}