ssi_core/
lexical.rs

1use serde::{Deserialize, Serialize};
2use std::{fmt, hash::Hash, ops::Deref, str::FromStr};
3
4/// Value with stable lexical representation.
5///
6/// Some value (such as date/time) can have multiple lexical representations.
7/// When dealing with externally generated data, it is sometime preferable to
8/// preserve the lexical representation we are provided with, even if it is not
9/// in canonical form.
10///
11/// This type is a wrapper around a value of type `T` that preserves any
12/// eventual lexical representation, found when deserializing (with
13/// [`Deserialize::deserialize`]) or parsing (with [`FromStr::from_str`] or
14/// [`str::parse`]).
15#[derive(Debug, Default, Clone)]
16pub struct Lexical<T> {
17    /// Logical value.
18    value: T,
19
20    /// Lexical value.
21    representation: Option<String>,
22}
23
24impl<T> Lexical<T> {
25    /// Wraps a value without any particular lexical representation.
26    ///
27    /// The [`fmt::Display`] or [`Serialize`] implementation of `T` will be used
28    /// as lexical representation.
29    pub fn new(value: T) -> Self {
30        Self {
31            value,
32            representation: None,
33        }
34    }
35
36    /// Wraps a value with the given lexical representation.
37    ///
38    /// This representation will be used in the [`fmt::Display`] and
39    /// [`Serialize`] implementations.
40    ///
41    /// It is a logical error to provide a representation that is not a valid
42    /// lexical representation of `value`.
43    pub fn new_with_representation(value: T, representation: String) -> Self {
44        Self {
45            value,
46            representation: Some(representation),
47        }
48    }
49
50    /// Wraps a value with the given lexical optional representation.
51    ///
52    /// If a representation is given, will be used in the [`fmt::Display`] and
53    /// [`Serialize`] implementations. Otherwise the `T` implementation of those
54    /// traits will be used.
55    ///
56    /// It is a logical error to provide a representation that is not a valid
57    /// lexical representation of `value`.
58    pub fn from_parts(value: T, representation: Option<String>) -> Self {
59        Self {
60            value,
61            representation,
62        }
63    }
64
65    /// Returns a reference to the inner value.
66    pub fn as_inner(&self) -> &T {
67        &self.value
68    }
69
70    /// Clones the inner value.
71    pub fn to_value(&self) -> T
72    where
73        T: Clone,
74    {
75        self.value.clone()
76    }
77
78    /// Returns ownership over the inner value.
79    pub fn into_value(self) -> T {
80        self.value
81    }
82
83    /// Breaks `self` into its constituting parts.
84    pub fn into_parts(self) -> (T, Option<String>) {
85        (self.value, self.representation)
86    }
87}
88
89impl<T> Deref for Lexical<T> {
90    type Target = T;
91
92    fn deref(&self) -> &Self::Target {
93        &self.value
94    }
95}
96
97impl<T> From<T> for Lexical<T> {
98    fn from(value: T) -> Self {
99        Self::new(value)
100    }
101}
102
103impl<T: PartialEq> PartialEq for Lexical<T> {
104    fn eq(&self, other: &Self) -> bool {
105        self.value.eq(&other.value)
106    }
107}
108
109impl<T: PartialEq> PartialEq<T> for Lexical<T> {
110    fn eq(&self, other: &T) -> bool {
111        self.value.eq(other)
112    }
113}
114
115impl<T: Eq> Eq for Lexical<T> {}
116
117impl<T: PartialOrd> PartialOrd for Lexical<T> {
118    fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
119        self.value.partial_cmp(&other.value)
120    }
121}
122
123impl<T: PartialOrd> PartialOrd<T> for Lexical<T> {
124    fn partial_cmp(&self, other: &T) -> Option<std::cmp::Ordering> {
125        self.value.partial_cmp(other)
126    }
127}
128
129impl<T: Ord> Ord for Lexical<T> {
130    fn cmp(&self, other: &Self) -> std::cmp::Ordering {
131        self.value.cmp(&other.value)
132    }
133}
134
135impl<T: Hash> Hash for Lexical<T> {
136    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
137        self.value.hash(state);
138    }
139}
140
141impl<T: FromStr> FromStr for Lexical<T> {
142    type Err = T::Err;
143
144    fn from_str(s: &str) -> Result<Self, Self::Err> {
145        s.parse()
146            .map(|value| Self::new_with_representation(value, s.to_owned()))
147    }
148}
149
150impl<T: Serialize> Serialize for Lexical<T> {
151    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
152    where
153        S: serde::Serializer,
154    {
155        match &self.representation {
156            Some(r) => r.serialize(serializer),
157            None => self.value.serialize(serializer),
158        }
159    }
160}
161
162impl<'de, T> Deserialize<'de> for Lexical<T>
163where
164    T: FromStr<Err: fmt::Display>,
165{
166    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
167    where
168        D: serde::Deserializer<'de>,
169    {
170        let representation = String::deserialize(deserializer)?;
171        representation
172            .parse()
173            .map_err(serde::de::Error::custom)
174            .map(|value| Self::new_with_representation(value, representation))
175    }
176}
177
178impl<T: fmt::Display> fmt::Display for Lexical<T> {
179    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
180        match &self.representation {
181            Some(r) => f.write_str(r),
182            None => self.value.fmt(f),
183        }
184    }
185}
186
187#[cfg(test)]
188mod tests {
189    use super::*;
190
191    #[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
192    struct I32String(i32);
193
194    impl FromStr for I32String {
195        type Err = <i32 as FromStr>::Err;
196
197        fn from_str(s: &str) -> Result<Self, Self::Err> {
198            s.parse().map(Self)
199        }
200    }
201
202    impl fmt::Display for I32String {
203        fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
204            self.0.fmt(f)
205        }
206    }
207
208    impl Serialize for I32String {
209        fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
210        where
211            S: serde::Serializer,
212        {
213            self.to_string().serialize(serializer)
214        }
215    }
216
217    #[test]
218    fn preserve_lexical_form() {
219        let n: Lexical<I32String> = "00001".parse().unwrap();
220        assert_eq!(n.to_string(), "00001");
221        assert_eq!(n, I32String(1));
222        assert_eq!(
223            serde_json::to_value(n).unwrap(),
224            serde_json::Value::String("00001".to_owned())
225        );
226
227        let m: Lexical<I32String> = serde_json::from_str("\"00001\"").unwrap();
228        assert_eq!(m.to_string(), "00001");
229        assert_eq!(m, I32String(1));
230    }
231}