Skip to main content

alpha2_media/
ids.rs

1//! Identity of a media source.
2
3use serde::{Deserialize, Deserializer, Serialize, de::Error as _};
4use std::fmt;
5
6macro_rules! define_id {
7    ($name:ident, $description:literal) => {
8        #[doc = $description]
9        #[derive(Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
10        #[serde(transparent)]
11        pub struct $name(String);
12
13        impl<'de> Deserialize<'de> for $name {
14            fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
15            where
16                D: Deserializer<'de>,
17            {
18                let value = String::deserialize(deserializer)?;
19                Self::new(value).map_err(D::Error::custom)
20            }
21        }
22
23        impl $name {
24            /// Creates an identifier from a non-empty, stable external value.
25            ///
26            /// UUIDs and ULIDs are both suitable values. The engine does not
27            /// generate IDs because identity belongs to the document author.
28            ///
29            /// # Errors
30            ///
31            /// Returns [`IdError::Empty`] when `value` is empty.
32            pub fn new(value: impl Into<String>) -> Result<Self, IdError> {
33                let value = value.into();
34                if value.is_empty() {
35                    Err(IdError::Empty)
36                } else {
37                    Ok(Self(value))
38                }
39            }
40
41            /// Returns this identifier as its serialized string value.
42            #[must_use]
43            pub fn as_str(&self) -> &str {
44                &self.0
45            }
46        }
47
48        #[cfg(feature = "schema")]
49        impl schemars::JsonSchema for $name {
50            fn schema_name() -> std::borrow::Cow<'static, str> {
51                stringify!($name).into()
52            }
53
54            fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
55                schemars::json_schema!({
56                    "description": $description,
57                    "type": "string",
58                    "minLength": 1,
59                })
60            }
61        }
62
63        impl fmt::Display for $name {
64            fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
65                formatter.write_str(&self.0)
66            }
67        }
68    };
69}
70
71define_id!(SourceId, "A stable media-source identifier.");
72
73/// An error returned when an identifier cannot be constructed.
74#[derive(Copy, Clone, Debug, Eq, PartialEq)]
75#[non_exhaustive]
76pub enum IdError {
77    /// IDs must contain at least one character.
78    Empty,
79}
80
81impl std::error::Error for IdError {}
82
83impl fmt::Display for IdError {
84    fn fmt(&self, formatter: &mut fmt::Formatter<'_>) -> fmt::Result {
85        formatter.write_str("identifier must not be empty")
86    }
87}