feat(domain): implement RelayId newtype with validation

Implement smart constructor that validates relay IDs are within valid 
range (1-8 for 8-channel relay controller). Add accessor method as_u8() 
for safe access to inner value. Add comprehensive documentation to satisfy 
clippy requirements.

TDD green phase: Tests from T017 now pass.

Ref: T018 (specs/001-modbus-relay-control/tasks.md)
This commit is contained in:
2026-01-03 22:34:36 +01:00
parent 86b194ad74
commit 0ec7fdf11b
5 changed files with 41 additions and 1 deletions

View File

@@ -0,0 +1,7 @@
/// Errors that can occur during relay controller operations.
#[derive(Debug, thiserror::Error)]
pub enum ControllerError {
/// The provided relay ID is outside the valid range (1-8).
#[error("Invalid relay ID: {0}")]
InvalidRelayId(u8),
}

View File

@@ -7,3 +7,5 @@
pub mod repository; pub mod repository;
/// Domain types for relay identification and control. /// Domain types for relay identification and control.
pub mod types; pub mod types;
/// Controller error types for relay operations.
pub mod controler;

View File

@@ -0,0 +1,2 @@
mod relayid;
pub use relayid::RelayId;

View File

@@ -1,3 +1,5 @@
use crate::domain::relay::controler::ControllerError;
/// Unique identifier for a relay in the system. /// Unique identifier for a relay in the system.
/// ///
/// Uses the newtype pattern to provide type safety and prevent mixing relay IDs /// Uses the newtype pattern to provide type safety and prevent mixing relay IDs
@@ -7,6 +9,33 @@
#[repr(transparent)] #[repr(transparent)]
pub struct RelayId(u8); pub struct RelayId(u8);
impl RelayId {
/// Creates a new `RelayId` from a relay channel number.
///
/// This is a smart constructor that validates the relay ID is within the
/// valid range (1-8) for an 8-channel relay controller.
///
/// # Errors
///
/// Returns `ControllerError::InvalidRelayId` if the ID is not in the range 1-8.
pub const fn new(id: u8) -> Result<Self, ControllerError> {
if id > 0 && id < 9 {
Ok(Self(id))
} else {
Err(ControllerError::InvalidRelayId(id))
}
}
/// Returns the inner `u8` value of the relay ID.
///
/// This accessor method provides access to the underlying relay channel number.
#[must_use]
pub const fn as_u8(&self) -> u8 {
self.0
}
}
impl std::fmt::Display for RelayId { impl std::fmt::Display for RelayId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.0.fmt(f) self.0.fmt(f)

View File

@@ -211,7 +211,7 @@
- **File**: src/domain/relay.rs - **File**: src/domain/relay.rs
- **Complexity**: Low | **Uncertainty**: Low - **Complexity**: Low | **Uncertainty**: Low
- [ ] **T018** [US1] [TDD] Implement RelayId newtype with validation - [x] **T018** [US1] [TDD] Implement RelayId newtype with validation
- #[repr(transparent)] newtype wrapping u8 - #[repr(transparent)] newtype wrapping u8
- Constructor validates 1..=8 range - Constructor validates 1..=8 range
- Implement Display, Debug, Clone, Copy, PartialEq, Eq - Implement Display, Debug, Clone, Copy, PartialEq, Eq