geode/parse/
reference.rs

1use lazy_static::lazy_static;
2use schemars::{schema::{InstanceType, Schema, SchemaObject, SingleOrVec, StringValidation, SubschemaValidation}, JsonSchema};
3use std::{fmt::Display, str::FromStr};
4use crate::parse::{QUANTITY_RE, QUANTITY_PATTERN};
5
6use regex::Regex;
7use serde::{de::Error, Deserialize, Serialize};
8use std::error::Error as StdError;
9use thiserror::Error;
10
11use super::RawRepr;
12
13#[derive(Debug, Clone, PartialEq)]
14pub struct WithReference<T> {
15    reference: bool,
16    value: T,
17    raw: String,
18}
19
20impl<T> JsonSchema for WithReference<T> {
21    fn schema_name() -> String {
22        String::from("ReferenceQuantity")
23    }
24
25    fn json_schema(gen: &mut schemars::gen::SchemaGenerator) -> schemars::schema::Schema {
26        let mut schema = SchemaObject::default();
27        //schema.instance_type = Some(SingleOrVec::Single(Box::new(InstanceType::String)));
28        schema.subschemas = Some(Box::new(SubschemaValidation {
29            one_of: Some(vec![
30                // Schema for string type
31                Schema::Object(SchemaObject {
32                    instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::String))),
33                    string: Some(Box::new(StringValidation {
34                        pattern: Some(QUANTITY_PATTERN.to_string()),
35                        ..Default::default()
36
37                    })),
38                    ..Default::default()
39                }),
40                // Schema for number type
41                Schema::Object(SchemaObject {
42                    instance_type: Some(SingleOrVec::Single(Box::new(InstanceType::Number))),
43                    ..Default::default()
44                }),
45            ]),
46            ..Default::default()
47        }));
48
49        Schema::Object(schema)
50    }
51}
52impl<T> RawRepr for WithReference<T> {
53    fn raw(&self) -> &str {
54        &self.raw
55    }
56}
57
58//const REFERENCE_PATTERN: &str = r"^\s*(?:(reference|ref)\s)?\s*([^\s].*?)\s*$"; 
59
60//lazy_static! {
61//    static ref REFERENCE_RE: Regex = Regex::new(formatcp!(r"^{QUANTITY_RE}"))
62//        .expect("Builtin regex should be valid");
63//}
64
65#[derive(Debug, Error)]
66pub enum ParseReferenceError<E: std::error::Error> {
67    #[error("invalid format, it should be [REF|REFERENCE] value")]
68    InvalidFormat,
69    #[error("invalid value: {0}")]
70    InvalidValue(#[from] E),
71}
72
73impl<T: FromStr> FromStr for WithReference<T>
74where
75    T::Err: StdError,
76{
77    type Err = ParseReferenceError<T::Err>;
78
79    fn from_str(s: &str) -> Result<Self, Self::Err> {
80        if let Some(captures) = QUANTITY_RE.captures(s) {
81            let reference = captures.get(1).is_some();
82            let value = &captures[2];
83            Ok(WithReference {
84                raw: captures
85                    .get(1)
86                    .map(|c| c.as_str())
87                    .unwrap_or("")
88                    .to_string(),
89                reference,
90                value: value.parse()?,
91            })
92        } else {
93            Err(ParseReferenceError::InvalidFormat)
94        }
95    }
96}
97
98impl<T: ToString> Serialize for WithReference<T> {
99    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
100    where
101        S: serde::Serializer,
102    {
103        let mut prefix = String::from("");
104        if self.reference {
105            prefix += &self.raw[..];
106            prefix += " ";
107        }
108        serializer.serialize_str(&format!("{prefix}{}", self.value.to_string()))
109    }
110}
111
112impl<'de, T: FromStr> Deserialize<'de> for WithReference<T>
113where
114    T::Err: StdError,
115{
116    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
117    where
118        D: serde::Deserializer<'de>,
119    {
120        let raw: String = Deserialize::deserialize(deserializer)?;
121        raw.parse().map_err(|e| D::Error::custom(e))
122    }
123}
124
125#[cfg(test)]
126mod tests {
127    use super::*;
128    use serde_yaml;
129    use std::str::FromStr;
130
131    #[derive(Debug, PartialEq, Serialize, Deserialize)]
132    struct TestValue(i32);
133
134    impl FromStr for TestValue {
135        type Err = std::num::ParseIntError;
136
137        fn from_str(s: &str) -> Result<Self, Self::Err> {
138            s.parse::<i32>().map(TestValue)
139        }
140    }
141
142    impl ToString for TestValue {
143        fn to_string(&self) -> String {
144            return self.0.to_string();
145        }
146    }
147
148    #[test]
149    fn test_parse_with_reference() {
150        let input = "ref 42";
151        let result: Result<WithReference<TestValue>, _> = input.parse();
152        assert!(result.is_ok());
153
154        let parsed = result.unwrap();
155        assert_eq!(parsed.raw, "ref");
156        assert!(parsed.reference);
157        assert_eq!(parsed.value, TestValue(42));
158    }
159
160    #[test]
161    fn test_parse_without_reference() {
162        let input = " 42";
163        let result: Result<WithReference<TestValue>, _> = input.parse();
164        assert!(result.is_ok());
165
166        let parsed = result.unwrap();
167        assert_eq!(parsed.raw, "");
168        assert!(!parsed.reference);
169        assert_eq!(parsed.value, TestValue(42));
170    }
171
172    #[test]
173    fn test_parse_invalid_format() {
174        let input = "invalid_format";
175        let result: Result<WithReference<TestValue>, _> = input.parse();
176        assert!(result.is_err());
177    }
178
179    #[test]
180    fn test_serialize_with_reference() {
181        let value = WithReference {
182            raw: "ref".to_string(),
183            reference: true,
184            value: 42,
185        };
186
187        let serialized = serde_yaml::to_string(&value).unwrap();
188        assert_eq!(serialized.trim(), "ref 42");
189    }
190    //
191    #[test]
192    fn test_serialize_without_reference() {
193        let value = WithReference {
194            raw: "".to_string(),
195            reference: false,
196            value: 42,
197        };
198
199        let serialized = serde_yaml::to_string(&value).unwrap();
200        assert_eq!(serialized.trim(), "'42'");
201    }
202    #[test]
203    fn test_serialize_with_reference_with_prefix() {
204        let value = WithReference {
205            raw: "reference".to_string(),
206            reference: true,
207            value: 100,
208        };
209
210        let serialized = serde_yaml::to_string(&value).unwrap();
211        assert_eq!(serialized.trim(), "reference 100");
212    }
213
214    #[test]
215    fn test_serialize_with_empty_string_value() {
216        let value = WithReference {
217            raw: "".to_string(),
218            reference: false,
219            value: "",
220        };
221
222        let serialized = serde_yaml::to_string(&value).unwrap();
223        assert_eq!(serialized.trim(), "''");
224    }
225
226    #[test]
227    fn test_serialize_with_special_characters() {
228        let value = WithReference {
229            raw: "ref".to_string(),
230            reference: true,
231            value: "!@#$%^&*()",
232        };
233
234        let serialized = serde_yaml::to_string(&value).unwrap();
235        assert_eq!(serialized.trim(), "ref !@#$%^&*()");
236    }
237
238    #[test]
239    fn test_serialize_with_complex_value() {
240        let value = WithReference {
241            raw: "".to_string(),
242            reference: false,
243            value: TestValue(12345),
244        };
245
246        let serialized = serde_yaml::to_string(&value).unwrap();
247        assert_eq!(serialized.trim(), "'12345'");
248    }
249}