feat(infrastructure): implement SQLite repository for relay labels

Add complete SQLite implementation of RelayLabelRepository trait with
all CRUD operations (get_label, save_label, delete_label, get_all_labels).

Key changes:
- Create infrastructure entities module with RelayLabelRecord struct
- Implement TryFrom traits for converting between database records and domain types
- Add From<sqlx::Error> and From<RelayLabelError> for RepositoryError
- Write comprehensive functional tests covering all repository operations
- Verify proper handling of edge cases (empty results, overwrites, max length)

TDD phase: GREEN - All repository trait tests now passing with SQLite implementation

Ref: T036 (specs/001-modbus-relay-control/tasks.md)
This commit is contained in:
2026-01-22 00:03:48 +01:00
parent 6d0a2bdb9e
commit 29ebe015fd
9 changed files with 661 additions and 8 deletions
+13 -1
View File
@@ -1,7 +1,7 @@
mod label;
pub use label::RelayLabelRepository;
use super::types::RelayId;
use super::types::{RelayId, RelayLabelError};
/// Errors that can occur during repository operations.
///
@@ -16,3 +16,15 @@ pub enum RepositoryError {
#[error("Relay not found: {0}")]
NotFound(RelayId),
}
impl From<sqlx::Error> for RepositoryError {
fn from(value: sqlx::Error) -> Self {
Self::DatabaseError(value.to_string())
}
}
impl From<RelayLabelError> for RepositoryError {
fn from(value: RelayLabelError) -> Self {
Self::DatabaseError(value.to_string())
}
}
+1 -1
View File
@@ -3,5 +3,5 @@ mod relaylabel;
mod relaystate;
pub use relayid::RelayId;
pub use relaylabel::RelayLabel;
pub use relaylabel::{RelayLabel, RelayLabelError};
pub use relaystate::RelayState;
@@ -8,10 +8,19 @@ use thiserror::Error;
#[repr(transparent)]
pub struct RelayLabel(String);
/// Errors that can occur when creating or validating relay labels.
#[derive(Debug, Error)]
pub enum RelayLabelError {
/// The label string is empty.
///
/// Relay labels must contain at least one character.
#[error("Label cannot be empty")]
Empty,
/// The label string exceeds the maximum allowed length.
///
/// Contains the actual length of the invalid label.
/// Maximum allowed length is 50 characters.
#[error("Label exceeds maximum length of 50 characters: {0}")]
TooLong(usize),
}