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.subschemas = Some(Box::new(SubschemaValidation {
29 one_of: Some(vec![
30 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::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#[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 #[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}