Skip to main content

use_db_row/
lib.rs

1#![forbid(unsafe_code)]
2#![doc = include_str!("../README.md")]
3
4//! Row metadata primitives for `RustUse`.
5
6use core::fmt;
7use std::error::Error;
8
9/// A row identifier label.
10#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
11pub struct RowId(String);
12
13impl RowId {
14    /// Creates a row identifier.
15    ///
16    /// # Errors
17    ///
18    /// Returns [`RowError`] when the identifier is empty or contains control characters.
19    pub fn new(input: impl AsRef<str>) -> Result<Self, RowError> {
20        validate_text(input.as_ref()).map(|value| Self(value.to_owned()))
21    }
22
23    /// Returns the row identifier.
24    #[must_use]
25    pub fn as_str(&self) -> &str {
26        &self.0
27    }
28}
29
30impl fmt::Display for RowId {
31    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
32        formatter.write_str(self.as_str())
33    }
34}
35
36/// A one-based row number.
37#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
38pub struct RowNumber(u64);
39
40impl RowNumber {
41    /// Creates a one-based row number.
42    #[must_use]
43    pub const fn new(value: u64) -> Option<Self> {
44        if value == 0 { None } else { Some(Self(value)) }
45    }
46
47    /// Returns the row number.
48    #[must_use]
49    pub const fn value(self) -> u64 {
50        self.0
51    }
52}
53
54/// A row count.
55#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
56pub struct RowCount(u64);
57
58impl RowCount {
59    /// Creates a row count.
60    #[must_use]
61    pub const fn new(value: u64) -> Self {
62        Self(value)
63    }
64
65    /// Returns the row count.
66    #[must_use]
67    pub const fn value(self) -> u64 {
68        self.0
69    }
70}
71
72/// A count of affected rows.
73#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
74pub struct AffectedRows(u64);
75
76impl AffectedRows {
77    /// Creates an affected-row count.
78    #[must_use]
79    pub const fn new(value: u64) -> Self {
80        Self(value)
81    }
82
83    /// Returns the affected-row count.
84    #[must_use]
85    pub const fn value(self) -> u64 {
86        self.0
87    }
88}
89
90/// Broad row status.
91#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
92pub enum RowStatus {
93    /// Row is active/current.
94    #[default]
95    Active,
96    /// Row is soft-deleted or removed.
97    Deleted,
98    /// Row is archived.
99    Archived,
100    /// Status is unknown.
101    Unknown,
102}
103
104/// Error returned by row primitives.
105#[derive(Clone, Copy, Debug, Eq, PartialEq)]
106pub enum RowError {
107    /// Text was empty.
108    Empty,
109    /// Text contained a control character.
110    ControlCharacter,
111}
112
113impl fmt::Display for RowError {
114    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
115        match self {
116            Self::Empty => formatter.write_str("row identifier cannot be empty"),
117            Self::ControlCharacter => {
118                formatter.write_str("row identifier cannot contain control characters")
119            },
120        }
121    }
122}
123
124impl Error for RowError {}
125
126fn validate_text(input: &str) -> Result<&str, RowError> {
127    if input.chars().any(char::is_control) {
128        return Err(RowError::ControlCharacter);
129    }
130    let trimmed = input.trim();
131    if trimmed.is_empty() {
132        return Err(RowError::Empty);
133    }
134    Ok(trimmed)
135}
136
137#[cfg(test)]
138mod tests {
139    use super::{AffectedRows, RowCount, RowError, RowId, RowNumber, RowStatus};
140
141    #[test]
142    fn stores_row_primitives() -> Result<(), RowError> {
143        let id = RowId::new("row-1")?;
144        let number = RowNumber::new(1).expect("nonzero row number");
145        let count = RowCount::new(3);
146        let affected = AffectedRows::new(2);
147
148        assert_eq!(id.to_string(), "row-1");
149        assert_eq!(number.value(), 1);
150        assert_eq!(count.value(), 3);
151        assert_eq!(affected.value(), 2);
152        assert_eq!(RowStatus::Active, RowStatus::default());
153        Ok(())
154    }
155}