nv_redfish_csdl_compiler/edmx/
attribute_values.rs1use crate::edmx::QualifiedTypeName;
19use crate::OneOrCollection;
20use serde::de::Error as DeError;
21use serde::de::Visitor;
22use serde::Deserialize;
23use serde::Deserializer;
24use std::fmt::Display;
25use std::fmt::Formatter;
26use std::fmt::Result as FmtResult;
27use std::str::FromStr;
28
29#[derive(Debug)]
30pub enum Error {
31 InvalidSimpleIdentifier(String),
32 InvalidQualifiedIdentifier(String),
33}
34
35impl Display for Error {
36 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
37 match self {
38 Self::InvalidSimpleIdentifier(id) => write!(f, "invalid simple identifier {id}"),
39 Self::InvalidQualifiedIdentifier(id) => write!(f, "invalid qualified identifier {id}"),
40 }
41 }
42}
43
44#[derive(Clone, Debug, PartialEq, Eq, Hash)]
46pub struct Namespace {
47 pub ids: Vec<SimpleIdentifier>,
48}
49
50impl Namespace {
51 #[must_use]
52 pub fn is_edm(&self) -> bool {
53 self.ids.len() == 1 && self.ids[0].inner() == "Edm"
54 }
55}
56
57impl FromStr for Namespace {
58 type Err = Error;
59 fn from_str(s: &str) -> Result<Self, Self::Err> {
60 Ok(Self {
61 ids: s
62 .split('.')
63 .map(SimpleIdentifier::from_str)
64 .collect::<Result<Vec<_>, _>>()?,
65 })
66 }
67}
68
69impl Display for Namespace {
70 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
71 let mut iter = self.ids.iter();
72 if let Some(v) = iter.next() {
73 v.fmt(f)?;
74 }
75 for v in iter {
76 ".".fmt(f)?;
77 v.fmt(f)?;
78 }
79 Ok(())
80 }
81}
82
83impl<'de> Deserialize<'de> for Namespace {
84 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
85 struct NsVisitor {}
86 impl Visitor<'_> for NsVisitor {
87 type Value = Namespace;
88
89 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> FmtResult {
90 formatter.write_str("Namespace string")
91 }
92 fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
93 value.parse().map_err(DeError::custom)
94 }
95 }
96
97 de.deserialize_string(NsVisitor {})
98 }
99}
100
101#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd, Hash)]
103pub struct SimpleIdentifier(String);
104
105impl SimpleIdentifier {
106 #[must_use]
107 pub const fn inner(&self) -> &String {
108 &self.0
109 }
110}
111
112impl Display for SimpleIdentifier {
113 fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
114 self.0.fmt(f)
115 }
116}
117
118impl AsRef<str> for SimpleIdentifier {
119 fn as_ref(&self) -> &str {
120 &self.0
121 }
122}
123
124impl FromStr for SimpleIdentifier {
125 type Err = Error;
126 fn from_str(s: &str) -> Result<Self, Self::Err> {
127 let mut chars = s.chars();
128
129 chars
134 .next()
135 .and_then(|first| {
136 if first.is_alphabetic() || first == '_' {
137 Some(())
138 } else {
139 None
140 }
141 })
142 .ok_or_else(|| Error::InvalidSimpleIdentifier(s.into()))?;
143
144 if chars.any(|c| !c.is_alphanumeric() && c != '_') {
145 Err(Error::InvalidSimpleIdentifier(s.into()))
146 } else {
147 Ok(Self(s.into()))
148 }
149 }
150}
151
152impl<'de> Deserialize<'de> for SimpleIdentifier {
153 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
154 struct SiVisitor {}
155 impl Visitor<'_> for SiVisitor {
156 type Value = SimpleIdentifier;
157
158 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> FmtResult {
159 formatter.write_str("SimpleIdentifier string")
160 }
161 fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
162 value.parse().map_err(DeError::custom)
163 }
164 }
165
166 de.deserialize_string(SiVisitor {})
167 }
168}
169
170#[derive(Debug, PartialEq, Eq, Hash)]
172pub struct QualifiedName {
173 pub namespace: Namespace,
174 pub name: SimpleIdentifier,
175}
176
177impl FromStr for QualifiedName {
178 type Err = Error;
179 fn from_str(s: &str) -> Result<Self, Self::Err> {
180 let mut ids = s
181 .split('.')
182 .map(SimpleIdentifier::from_str)
183 .collect::<Result<Vec<_>, _>>()
184 .map_err(|_| Error::InvalidQualifiedIdentifier(s.into()))?;
185 let name = ids
186 .pop()
187 .ok_or_else(|| Error::InvalidQualifiedIdentifier(s.into()))?;
188 Ok(Self {
189 namespace: Namespace { ids },
190 name,
191 })
192 }
193}
194
195impl<'de> Deserialize<'de> for QualifiedName {
196 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
197 struct QnVisitor {}
198 impl Visitor<'_> for QnVisitor {
199 type Value = QualifiedName;
200
201 fn expecting(&self, formatter: &mut std::fmt::Formatter) -> FmtResult {
202 formatter.write_str("QualifiedName string")
203 }
204 fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
205 value.parse().map_err(DeError::custom)
206 }
207 }
208
209 de.deserialize_string(QnVisitor {})
210 }
211}
212
213pub type TypeName = OneOrCollection<QualifiedTypeName>;
215
216impl TypeName {
217 #[must_use]
218 pub const fn qualified_type_name(&self) -> &QualifiedTypeName {
219 self.inner()
220 }
221}
222
223impl FromStr for TypeName {
224 type Err = Error;
225 fn from_str(s: &str) -> Result<Self, Self::Err> {
226 const COLLECTION_PREFIX: &str = "Collection(";
227 const COLLECTION_SUFFIX: &str = ")";
228 if s.starts_with(COLLECTION_PREFIX) && s.ends_with(COLLECTION_SUFFIX) {
229 let qtype = s[COLLECTION_PREFIX.len()..s.len() - COLLECTION_SUFFIX.len()].parse()?;
230 Ok(Self::Collection(qtype))
231 } else {
232 Ok(Self::One(s.parse()?))
233 }
234 }
235}
236
237impl<'de> Deserialize<'de> for TypeName {
238 fn deserialize<D: Deserializer<'de>>(de: D) -> Result<Self, D::Error> {
239 struct QnVisitor {}
240 impl Visitor<'_> for QnVisitor {
241 type Value = TypeName;
242
243 fn expecting(&self, formatter: &mut Formatter) -> FmtResult {
244 formatter.write_str("property type string")
245 }
246
247 fn visit_str<E: DeError>(self, value: &str) -> Result<Self::Value, E> {
248 value.parse().map_err(DeError::custom)
249 }
250 }
251
252 de.deserialize_string(QnVisitor {})
253 }
254}
255
256#[cfg(test)]
257mod tests {
258 use super::*;
259 use serde_json::from_str as json_from_str;
260
261 #[test]
262 fn test_namespace_valid() {
263 let valid_cases = vec![
265 "Namespace",
266 "My.Namespace",
267 "My.Complex.Namespace",
268 "Edm", ];
270
271 for case in valid_cases {
272 let ns = Namespace::from_str(case);
273 assert!(ns.is_ok(), "Failed to parse valid Namespace: {}", case);
274
275 let ns = ns.unwrap();
277 let expected_count = case.chars().filter(|c| *c == '.').count() + 1;
278 assert_eq!(ns.ids.len(), expected_count);
279 }
280 }
281
282 #[test]
283 fn test_namespace_invalid() {
284 let invalid_cases = vec![
285 "Invalid.123Name", "Namespace.", ".Namespace", "Namespace..Name", "", ];
291
292 for case in invalid_cases {
293 assert!(
294 Namespace::from_str(case).is_err(),
295 "Should reject invalid Namespace: {}",
296 case
297 );
298 }
299 }
300
301 #[test]
302 fn test_namespace_is_edm() {
303 let edm = Namespace::from_str("Edm").unwrap();
304 assert!(edm.is_edm());
305
306 let not_edm = vec![
307 Namespace::from_str("NotEdm").unwrap(),
308 Namespace::from_str("Edm.Something").unwrap(),
309 Namespace::from_str("Something.Edm").unwrap(),
310 ];
311
312 for ns in not_edm {
313 assert!(!ns.is_edm());
314 }
315 }
316
317 #[test]
318 fn test_namespace_display() {
319 let test_cases = vec![
320 ("SingleNamespace", "SingleNamespace"),
321 ("My.Namespace", "My.Namespace"),
322 ("Complex.Name.Space", "Complex.Name.Space"),
323 ];
324
325 for (input, expected) in test_cases {
326 let ns = Namespace::from_str(input).unwrap();
327 assert_eq!(ns.to_string(), expected);
328 }
329 }
330
331 #[test]
332 fn test_namespace_deserialize() {
333 let json = r#""My.Valid.Namespace""#;
334 let ns: Namespace = json_from_str(json).expect("Should deserialize valid Namespace");
335 assert_eq!(ns.ids.len(), 3);
336
337 let json_invalid = r#""Invalid..Namespace""#;
338 let result: Result<Namespace, _> = json_from_str(json_invalid);
339 assert!(result.is_err());
340 }
341
342 #[test]
343 fn test_simple_identifier_valid() {
344 let valid_cases = vec![
347 "Name",
348 "name",
349 "_name",
350 "Name123",
351 "Name_with_underscores",
352 "a", ];
354
355 for case in valid_cases {
356 assert!(
357 SimpleIdentifier::from_str(case).is_ok(),
358 "Failed to parse valid SimpleIdentifier: {}",
359 case
360 );
361 }
362 }
363
364 #[test]
365 fn test_simple_identifier_invalid() {
366 let invalid_cases = vec![
368 "123Name", "Name-with-hyphens", "Name.with.dots", "Name with spaces", "", "$Name", ];
375
376 for case in invalid_cases {
377 assert!(
378 SimpleIdentifier::from_str(case).is_err(),
379 "Should reject invalid SimpleIdentifier: {}",
380 case
381 );
382 }
383 }
384
385 #[test]
386 fn test_simple_identifier_display() {
387 let id = SimpleIdentifier::from_str("TestId").unwrap();
388 assert_eq!(id.to_string(), "TestId");
389 }
390
391 #[test]
392 fn test_simple_identifier_deserialize() {
393 let json = r#""ValidName""#;
395 let id: SimpleIdentifier =
396 json_from_str(json).expect("Should deserialize valid SimpleIdentifier");
397 assert_eq!(id.inner(), "ValidName");
398
399 let json_invalid = r#""Invalid-Name""#;
400 let result: Result<SimpleIdentifier, _> = json_from_str(json_invalid);
401 assert!(result.is_err());
402 }
403
404 #[test]
405 fn test_qualified_name_valid() {
406 let valid_cases = vec![
408 "Namespace.Name",
409 "My.Namespace.Name",
410 "Complex.Namespace.Structure.Name",
411 ];
412
413 for case in valid_cases {
414 let qn = QualifiedName::from_str(case);
415 assert!(qn.is_ok(), "Failed to parse valid QualifiedName: {}", case);
416
417 let qn = qn.unwrap();
419 let parts: Vec<&str> = case.split('.').collect();
420 assert_eq!(qn.name.to_string(), *parts.last().unwrap());
421
422 assert_eq!(qn.namespace.ids.len(), parts.len() - 1);
424 }
425 }
426
427 #[test]
428 fn test_qualified_name_invalid() {
429 let invalid_cases = vec![
430 "Invalid.123Name", "Name-with-hyphens", "Namespace.", ".Namespace", "Namespace..Name", "", "Name with spaces", "Name.with.123invalid", ];
439
440 for case in invalid_cases {
441 assert!(
442 QualifiedName::from_str(case).is_err(),
443 "Should reject invalid QualifiedName: {}",
444 case
445 );
446 }
447 }
448
449 #[test]
450 fn test_qualified_name_deserialize() {
451 let json = r#""My.Valid.Namespace.Name""#;
452 let qn: QualifiedName =
453 json_from_str(json).expect("Should deserialize valid QualifiedName");
454 assert_eq!(qn.name.to_string(), "Name");
455 assert_eq!(qn.namespace.ids.len(), 3);
456
457 let json_invalid = r#""Invalid..Name""#; let result: Result<QualifiedName, _> = json_from_str(json_invalid);
459 assert!(result.is_err());
460 }
461
462 #[test]
463 fn test_type_name_valid() {
464 let valid_simple_cases = vec!["Edm.String", "My.Namespace.Type"];
466
467 let valid_collection_cases =
468 vec!["Collection(Edm.String)", "Collection(My.Namespace.Type)"];
469
470 for case in valid_simple_cases {
472 let tn = TypeName::from_str(case);
473 assert!(tn.is_ok(), "Failed to parse valid TypeName: {}", case);
474
475 assert!(
476 matches!(tn.unwrap(), TypeName::One(_)),
477 "Simple TypeName parsed as Collection: {}",
478 case
479 );
480 }
481
482 for case in valid_collection_cases {
484 let tn = TypeName::from_str(case);
485 assert!(
486 tn.is_ok(),
487 "Failed to parse valid Collection TypeName: {}",
488 case
489 );
490
491 assert!(
492 matches!(tn.unwrap(), TypeName::Collection(_)),
493 "Collection TypeName parsed as simple: {}",
494 case
495 );
496 }
497 }
498
499 #[test]
500 fn test_type_name_invalid() {
501 let invalid_cases = vec![
502 "Collection()", "Collection(Edm/Invalid)", "Collection(Edm.String", "CollectionEdm.String)", "Collection Edm.String", ];
508
509 for case in invalid_cases {
510 assert!(
511 TypeName::from_str(case).is_err(),
512 "Should reject invalid TypeName: {}",
513 case
514 );
515 }
516 }
517
518 #[test]
519 fn test_type_name_deserialize() {
520 let simple_json = r#""Edm.String""#;
521 let simple: TypeName =
522 json_from_str(simple_json).expect("Should deserialize valid TypeName");
523
524 assert!(
525 matches!(simple, TypeName::One(_)),
526 "Simple TypeName deserialized as Collection"
527 );
528
529 let collection_json = r#""Collection(Edm.String)""#;
530 let collection: TypeName =
531 json_from_str(collection_json).expect("Should deserialize valid Collection TypeName");
532
533 assert!(
534 matches!(collection, TypeName::Collection(_)),
535 "Collection TypeName deserialized as simple"
536 );
537 }
538
539 #[test]
541 fn test_error_display() {
542 let simple_id_error = Error::InvalidSimpleIdentifier("123invalid".to_string());
543 let qualified_id_error =
544 Error::InvalidQualifiedIdentifier("invalid..qualified".to_string());
545
546 assert_eq!(
547 simple_id_error.to_string(),
548 "invalid simple identifier 123invalid"
549 );
550 assert_eq!(
551 qualified_id_error.to_string(),
552 "invalid qualified identifier invalid..qualified"
553 );
554 }
555}