schematic_types/
strings.rs

1use crate::*;
2use std::fmt;
3use std::net::{Ipv4Addr, Ipv6Addr};
4use std::path::{Path, PathBuf};
5use std::time::{Duration, SystemTime};
6
7#[derive(Clone, Debug, Default, PartialEq)]
8#[cfg_attr(feature = "serde", derive(serde::Deserialize, serde::Serialize))]
9pub struct StringType {
10    #[cfg_attr(
11        feature = "serde",
12        serde(default, skip_serializing_if = "Option::is_none")
13    )]
14    pub default: Option<LiteralValue>,
15
16    #[cfg_attr(
17        feature = "serde",
18        serde(default, skip_serializing_if = "Option::is_none")
19    )]
20    pub enum_values: Option<Vec<String>>,
21
22    #[cfg_attr(
23        feature = "serde",
24        serde(default, skip_serializing_if = "Option::is_none")
25    )]
26    pub format: Option<String>,
27
28    #[cfg_attr(
29        feature = "serde",
30        serde(default, skip_serializing_if = "Option::is_none")
31    )]
32    pub max_length: Option<usize>,
33
34    #[cfg_attr(
35        feature = "serde",
36        serde(default, skip_serializing_if = "Option::is_none")
37    )]
38    pub min_length: Option<usize>,
39
40    #[cfg_attr(
41        feature = "serde",
42        serde(default, skip_serializing_if = "Option::is_none")
43    )]
44    pub pattern: Option<String>,
45}
46
47impl StringType {
48    /// Create a string schema with the provided default value.
49    pub fn new(value: impl AsRef<str>) -> Self {
50        StringType {
51            default: Some(LiteralValue::String(value.as_ref().to_owned())),
52            ..StringType::default()
53        }
54    }
55}
56
57impl fmt::Display for StringType {
58    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
59        if self.max_length.is_some_and(|max| max == 1)
60            && self.min_length.is_some_and(|min| min == 1)
61        {
62            write!(f, "char")
63        } else if let Some(format) = &self.format {
64            write!(f, "string:{format}")
65        } else {
66            write!(f, "string")
67        }
68    }
69}
70
71macro_rules! impl_string {
72    ($type:ty) => {
73        impl Schematic for $type {
74            fn build_schema(mut schema: SchemaBuilder) -> Schema {
75                schema.string_default()
76            }
77        }
78    };
79}
80
81macro_rules! impl_string_format {
82    ($type:ty, $format:expr_2021) => {
83        impl Schematic for $type {
84            fn build_schema(mut schema: SchemaBuilder) -> Schema {
85                schema.string(StringType {
86                    format: Some($format.into()),
87                    ..StringType::default()
88                })
89            }
90        }
91    };
92}
93
94impl Schematic for char {
95    fn build_schema(mut schema: SchemaBuilder) -> Schema {
96        schema.string(StringType {
97            max_length: Some(1),
98            min_length: Some(1),
99            ..StringType::default()
100        })
101    }
102}
103
104impl_string!(str);
105impl_string!(&str);
106impl_string!(String);
107
108impl_string_format!(Path, "path");
109impl_string_format!(&Path, "path");
110impl_string_format!(PathBuf, "path");
111
112impl_string_format!(Ipv4Addr, "ipv4");
113impl_string_format!(Ipv6Addr, "ipv6");
114
115impl_string_format!(SystemTime, "time");
116impl_string_format!(Duration, "duration");