Skip to main content

commit_bridge/domain/
non_empty_string.rs

1//! Domain type to represent a non-empty string.
2
3use crate::error::ValidationError;
4use serde::{Deserialize, Serialize};
5use validator::Validate;
6
7/// A string that is guaranteed to be non-empty.
8#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Eq)]
9#[serde(try_from = "String", into = "String")]
10pub struct NonEmptyString(String);
11
12impl NonEmptyString {
13    /// Create a new `NonEmptyString`.
14    pub fn new(value: String) -> Result<Self, ValidationError> {
15        Self::try_from(value)
16    }
17
18    /// Get the inner string.
19    pub fn into_inner(self) -> String {
20        self.0
21    }
22
23    /// Get the string as a slice.
24    pub fn as_str(&self) -> &str {
25        &self.0
26    }
27}
28
29impl std::fmt::Display for NonEmptyString {
30    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
31        write!(f, "{}", self.0)
32    }
33}
34
35impl AsRef<str> for NonEmptyString {
36    fn as_ref(&self) -> &str {
37        &self.0
38    }
39}
40
41impl TryFrom<String> for NonEmptyString {
42    type Error = ValidationError;
43
44    fn try_from(value: String) -> Result<Self, Self::Error> {
45        #[derive(Validate)]
46        struct NonEmptyStringValidator {
47            #[validate(length(min = 1))]
48            val: String,
49        }
50
51        let validator = NonEmptyStringValidator { val: value.clone() };
52        if let Err(e) = validator.validate() {
53            tracing::warn!("Validation failed for NonEmptyString. Error: {}", e);
54            return Err(ValidationError::InvalidValue(format!(
55                "String must not be empty: {}",
56                value
57            )));
58        }
59
60        Ok(NonEmptyString(value))
61    }
62}
63
64impl std::ops::Deref for NonEmptyString {
65    type Target = str;
66
67    fn deref(&self) -> &Self::Target {
68        &self.0
69    }
70}
71
72impl PartialEq<str> for NonEmptyString {
73    fn eq(&self, other: &str) -> bool {
74        self.0 == other
75    }
76}
77
78impl PartialEq<NonEmptyString> for str {
79    fn eq(&self, other: &NonEmptyString) -> bool {
80        self == other.0
81    }
82}
83
84impl PartialEq<String> for NonEmptyString {
85    fn eq(&self, other: &String) -> bool {
86        &self.0 == other
87    }
88}
89
90impl PartialEq<NonEmptyString> for String {
91    fn eq(&self, other: &NonEmptyString) -> bool {
92        self == &other.0
93    }
94}
95
96impl From<NonEmptyString> for String {
97    fn from(val: NonEmptyString) -> Self {
98        val.0
99    }
100}