aquarium_control/mineral/
balling_error.rs

1/* Copyright 2025 Uwe Martin
2
3Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the “Software”), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
4
5The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
6
7THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
8*/
9
10//! Defines the custom error types for the Balling mineral dosing system.
11//!
12//! This module contains the `BallingError` enum, which consolidates all potential
13//! failures that can occur during the initialization and execution of the `Balling`
14//! dosing controller. By using a dedicated, descriptive error type, the system can
15//! provide clear diagnostics when the dosing logic fails.
16//!
17//! ## Design and Purpose
18//!
19//! The `BallingError` enum is designed using the `thiserror` crate to provide
20//! structured and informative error messages.
21//!
22//! - **Source Chaining**: For errors originating from the database layer (e.g.,
23//!   `SetValueRetrievalError`), the `#[source]` attribute preserves the underlying
24//!   `SqlInterfaceError`. This creates a full error chain, which is invaluable for
25//!   debugging the root cause of a database failure.
26//!
27//! - **Rich Context**: Each error variant is designed to include important context,
28//!   such as the code `location` and the specific `pump_id` that was involved
29//!   when the error occurred.
30//!
31//! ## Error Categories
32//!
33//! The errors fall into two main categories:
34//!
35//! 1.  **Configuration Validation Errors**: These errors occur at startup when the
36//!     `Balling::new()` constructor validates the configuration. They prevent the
37//!     application from starting with an invalid or illogical setup.
38//!     - `ScheduleCheckIntervalZero`
39//!     - `DosingIntervalShorterThanCheckInterval`
40//!     - `InvalidDosingInterval`
41//!
42//! 2.  **Runtime Errors**: These errors can occur while the `Balling` thread is
43//!     running.
44//!     - `SetValueRetrievalError`: A failure to fetch pump settings from the database.
45//!     - `ReadDurationSinceLastDosingFailure`: A failure to calculate the time since
46//!       the last dosing event.
47//!     - `CountdownCalculationInvalidPumpId`: An internal logic error indicating an
48//!       invalid pump ID was used.
49
50use crate::database::sql_interface_error::SqlInterfaceError;
51use thiserror::Error;
52
53/// Contains error definitions for Balling
54#[derive(Debug, Error)]
55pub enum BallingError {
56    /// Balling check interval is zero.
57    #[error("[{0}] schedule check interval is zero - please assign a value > 0")]
58    ScheduleCheckIntervalZero(String),
59
60    /// Could not get set values for a pump from database.
61    #[error("[{location}] Could not get set values for pump {pump_id} from database.")]
62    SetValueRetrievalError {
63        location: String,
64        pump_id: i64,
65
66        #[source]
67        source: Box<SqlInterfaceError>,
68    },
69
70    /// Invalid dosing interval in configuration file
71    #[error("[{0}] Invalid dosing interval for pump {1} in configuration file: Dosing interval ({2}) is shorter than check interval ({3}).")]
72    DosingIntervalShorterThanCheckInterval(String, u32, u32, u32),
73
74    /// Countdown calculation encountered invalid pump id.
75    #[error("[{0}] Countdown calculation encountered invalid pump id {1}")]
76    CountdownCalculationInvalidPumpId(String, i64),
77
78    /// Invalid dosing interval in configuration.
79    #[error("[{0}] Invalid dosing interval for pump #{1} in configuration.")]
80    InvalidDosingInterval(String, i64),
81
82    /// Reading duration since the last dosing event from the database failed.
83    #[error("Reading duration since last dosing event for pump {pump_id} from database failed.")]
84    ReadDurationSinceLastDosingFailure {
85        location: String,
86        pump_id: i64,
87
88        #[source]
89        source: Box<SqlInterfaceError>,
90    },
91}