#![forbid(unsafe_code)]
#![doc = include_str!("../README.md")]
use core::fmt;
use std::error::Error;
#[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RowId(String);
impl RowId {
pub fn new(input: impl AsRef<str>) -> Result<Self, RowError> {
validate_text(input.as_ref()).map(|value| Self(value.to_owned()))
}
#[must_use]
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for RowId {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
formatter.write_str(self.as_str())
}
}
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RowNumber(u64);
impl RowNumber {
#[must_use]
pub const fn new(value: u64) -> Option<Self> {
if value == 0 { None } else { Some(Self(value)) }
}
#[must_use]
pub const fn value(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct RowCount(u64);
impl RowCount {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn value(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub struct AffectedRows(u64);
impl AffectedRows {
#[must_use]
pub const fn new(value: u64) -> Self {
Self(value)
}
#[must_use]
pub const fn value(self) -> u64 {
self.0
}
}
#[derive(Clone, Copy, Debug, Default, Eq, Hash, Ord, PartialEq, PartialOrd)]
pub enum RowStatus {
#[default]
Active,
Deleted,
Archived,
Unknown,
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum RowError {
Empty,
ControlCharacter,
}
impl fmt::Display for RowError {
fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Self::Empty => formatter.write_str("row identifier cannot be empty"),
Self::ControlCharacter => {
formatter.write_str("row identifier cannot contain control characters")
},
}
}
}
impl Error for RowError {}
fn validate_text(input: &str) -> Result<&str, RowError> {
if input.chars().any(char::is_control) {
return Err(RowError::ControlCharacter);
}
let trimmed = input.trim();
if trimmed.is_empty() {
return Err(RowError::Empty);
}
Ok(trimmed)
}
#[cfg(test)]
mod tests {
use super::{AffectedRows, RowCount, RowError, RowId, RowNumber, RowStatus};
#[test]
fn stores_row_primitives() -> Result<(), RowError> {
let id = RowId::new("row-1")?;
let number = RowNumber::new(1).expect("nonzero row number");
let count = RowCount::new(3);
let affected = AffectedRows::new(2);
assert_eq!(id.to_string(), "row-1");
assert_eq!(number.value(), 1);
assert_eq!(count.value(), 3);
assert_eq!(affected.value(), 2);
assert_eq!(RowStatus::Active, RowStatus::default());
Ok(())
}
}