p2panda_rs/schema/
schema_description.rs

1// SPDX-License-Identifier: AGPL-3.0-or-later
2
3use std::fmt;
4use std::fmt::Display;
5use std::str::FromStr;
6
7use serde::{Deserialize, Serialize, Serializer};
8
9use crate::schema::error::SchemaDescriptionError;
10use crate::schema::validate::validate_description;
11use crate::Validate;
12
13/// The description of a schema which adheres to specification requirements. Used in the
14/// construction of `Schema`.
15#[derive(Clone, Debug, PartialEq, Eq, Hash)]
16pub struct SchemaDescription(String);
17
18impl SchemaDescription {
19    /// Construct and validate new schema description from a string.
20    pub fn new(description: &str) -> Result<Self, SchemaDescriptionError> {
21        let description = Self(description.to_owned());
22        description.validate()?;
23        Ok(description)
24    }
25}
26
27impl Validate for SchemaDescription {
28    type Error = SchemaDescriptionError;
29
30    /// Perform validation on the description string.
31    ///
32    /// 1. It consists of unicode characters
33    /// 2. ... and must be at most 256 characters long
34    fn validate(&self) -> Result<(), Self::Error> {
35        if !validate_description(&self.0) {
36            return Err(SchemaDescriptionError::TooLongSchemaDescription);
37        }
38        Ok(())
39    }
40}
41
42impl FromStr for SchemaDescription {
43    type Err = SchemaDescriptionError;
44
45    fn from_str(s: &str) -> Result<Self, Self::Err> {
46        Self::new(s)
47    }
48}
49
50impl Display for SchemaDescription {
51    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
52        write!(f, "{}", self.0)
53    }
54}
55
56impl Serialize for SchemaDescription {
57    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
58    where
59        S: Serializer,
60    {
61        serializer.serialize_str(&self.to_string())
62    }
63}
64
65impl<'de> Deserialize<'de> for SchemaDescription {
66    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
67    where
68        D: serde::Deserializer<'de>,
69    {
70        // Deserialize into string
71        let name: String = Deserialize::deserialize(deserializer)?;
72
73        // Check format
74        let schema_name = SchemaDescription::new(&name).map_err(|err| {
75            serde::de::Error::custom(format!("invalid schema description, {}", err))
76        })?;
77
78        Ok(schema_name)
79    }
80}
81
82#[cfg(test)]
83mod test {
84    use rstest::rstest;
85
86    use super::SchemaDescription;
87
88    #[rstest]
89    #[case(
90        "The kangaroo is a marsupial from the family Macropodidae
91           (macropods, meaning large foot)"
92    )]
93    #[case("%*&______@@@@@[[}}}{}}}}}}}&}{&{&{&{&{&}}}}}]]")]
94    #[should_panic]
95    #[case(
96        "In common use the term is used to describe the largest species from this
97           family, the red kangaroo, as well as the antilopine kangaroo, eastern grey
98           kangaroo, and western grey kangaroo! Kangaroos have large, powerful hind legs,
99           large feet adapted for leaping, a long muscular tail for balance, and a small
100           head. Like most marsupials, female kangaroos have a pouch called a marsupium
101           in which joeys complete postnatal development."
102    )]
103    fn validates_descriptions(#[case] description_str: &str) {
104        assert!(SchemaDescription::new(description_str).is_ok());
105    }
106}