1extern crate alloc;
8
9use alloc::borrow::Cow;
10use alloc::collections::BTreeMap;
11use alloc::collections::BTreeSet;
12use alloc::format;
13use alloc::string::String;
14use alloc::string::ToString;
15use alloc::vec::Vec;
16use core::fmt;
17
18use facet_core::{Field, Shape};
19
20#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
26#[non_exhaustive]
27pub enum FieldCategory {
28 #[default]
31 Flat,
32 Attribute,
34 Element,
36 Text,
38 Tag,
40 Elements,
42}
43
44impl FieldCategory {
45 pub fn from_field_dom(field: &Field) -> Option<Self> {
50 if field.is_flattened() {
51 return None;
53 }
54 if field.is_attribute() {
55 Some(FieldCategory::Attribute)
56 } else if field.is_text() {
57 Some(FieldCategory::Text)
58 } else if field.is_tag() {
59 Some(FieldCategory::Tag)
60 } else if field.is_elements() {
61 Some(FieldCategory::Elements)
62 } else {
63 Some(FieldCategory::Element)
65 }
66 }
67}
68
69#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
74#[non_exhaustive]
75pub enum FieldKey<'a> {
76 Flat(Cow<'a, str>),
78 Dom(FieldCategory, Cow<'a, str>),
80}
81
82impl<'a> FieldKey<'a> {
83 pub fn flat(name: impl Into<Cow<'a, str>>) -> Self {
85 FieldKey::Flat(name.into())
86 }
87
88 pub fn attribute(name: impl Into<Cow<'a, str>>) -> Self {
90 FieldKey::Dom(FieldCategory::Attribute, name.into())
91 }
92
93 pub fn element(name: impl Into<Cow<'a, str>>) -> Self {
95 FieldKey::Dom(FieldCategory::Element, name.into())
96 }
97
98 pub fn text() -> Self {
100 FieldKey::Dom(FieldCategory::Text, Cow::Borrowed(""))
101 }
102
103 pub fn tag() -> Self {
105 FieldKey::Dom(FieldCategory::Tag, Cow::Borrowed(""))
106 }
107
108 pub fn elements() -> Self {
110 FieldKey::Dom(FieldCategory::Elements, Cow::Borrowed(""))
111 }
112
113 pub fn name(&self) -> &str {
115 match self {
116 FieldKey::Flat(name) => name.as_ref(),
117 FieldKey::Dom(_, name) => name.as_ref(),
118 }
119 }
120
121 pub fn category(&self) -> Option<FieldCategory> {
123 match self {
124 FieldKey::Flat(_) => None,
125 FieldKey::Dom(cat, _) => Some(*cat),
126 }
127 }
128
129 pub fn into_owned(self) -> FieldKey<'static> {
131 match self {
132 FieldKey::Flat(name) => FieldKey::Flat(Cow::Owned(name.into_owned())),
133 FieldKey::Dom(cat, name) => FieldKey::Dom(cat, Cow::Owned(name.into_owned())),
134 }
135 }
136}
137
138impl<'a> From<&'a str> for FieldKey<'a> {
140 fn from(s: &'a str) -> Self {
141 FieldKey::Flat(Cow::Borrowed(s))
142 }
143}
144
145impl From<String> for FieldKey<'static> {
146 fn from(s: String) -> Self {
147 FieldKey::Flat(Cow::Owned(s))
148 }
149}
150
151impl<'a> From<&'a String> for FieldKey<'a> {
152 fn from(s: &'a String) -> Self {
153 FieldKey::Flat(Cow::Borrowed(s.as_str()))
154 }
155}
156
157impl<'a> From<Cow<'a, str>> for FieldKey<'a> {
158 fn from(s: Cow<'a, str>) -> Self {
159 FieldKey::Flat(s)
160 }
161}
162
163impl fmt::Display for FieldKey<'_> {
164 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
165 match self {
166 FieldKey::Flat(name) => write!(f, "{}", name),
167 FieldKey::Dom(cat, name) => write!(f, "{:?}:{}", cat, name),
168 }
169 }
170}
171
172pub type KeyPath = Vec<&'static str>;
176
177pub type DomKeyPath = Vec<FieldKey<'static>>;
180
181#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
183#[non_exhaustive]
184pub enum PathSegment {
185 Field(&'static str),
187 Variant(&'static str, &'static str),
189}
190
191#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
193pub struct FieldPath {
194 segments: Vec<PathSegment>,
195}
196
197impl fmt::Debug for FieldPath {
198 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
199 write!(f, "FieldPath(")?;
200 for (i, seg) in self.segments.iter().enumerate() {
201 if i > 0 {
202 write!(f, ".")?;
203 }
204 match seg {
205 PathSegment::Field(name) => write!(f, "{name}")?,
206 PathSegment::Variant(field, variant) => write!(f, "{field}::{variant}")?,
207 }
208 }
209 write!(f, ")")
210 }
211}
212
213impl fmt::Display for FieldPath {
214 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
215 let mut first = true;
216 for seg in &self.segments {
217 match seg {
218 PathSegment::Field(name) => {
219 if !first {
220 write!(f, ".")?;
221 }
222 write!(f, "{name}")?;
223 first = false;
224 }
225 PathSegment::Variant(_, _) => {
226 }
228 }
229 }
230 Ok(())
231 }
232}
233
234impl FieldPath {
235 pub const fn empty() -> Self {
237 Self {
238 segments: Vec::new(),
239 }
240 }
241
242 pub const fn depth(&self) -> usize {
244 self.segments.len()
245 }
246
247 pub fn push_field(&self, name: &'static str) -> Self {
249 let mut new = self.clone();
250 new.segments.push(PathSegment::Field(name));
251 new
252 }
253
254 pub fn push_variant(&self, field_name: &'static str, variant_name: &'static str) -> Self {
256 let mut new = self.clone();
257 new.segments
258 .push(PathSegment::Variant(field_name, variant_name));
259 new
260 }
261
262 pub fn parent(&self) -> Self {
264 let mut new = self.clone();
265 new.segments.pop();
266 new
267 }
268
269 pub fn segments(&self) -> &[PathSegment] {
271 &self.segments
272 }
273
274 pub fn last(&self) -> Option<&PathSegment> {
276 self.segments.last()
277 }
278}
279
280#[derive(Debug, Clone)]
282pub struct VariantSelection {
283 pub path: FieldPath,
285 pub enum_name: &'static str,
287 pub variant_name: &'static str,
289}
290
291#[derive(Debug, Clone)]
293pub struct FieldInfo {
294 pub serialized_name: &'static str,
296
297 pub path: FieldPath,
299
300 pub required: bool,
302
303 pub value_shape: &'static Shape,
305
306 pub field: &'static Field,
308
309 pub category: FieldCategory,
311}
312
313impl PartialEq for FieldInfo {
314 fn eq(&self, other: &Self) -> bool {
315 self.serialized_name == other.serialized_name
316 && self.path == other.path
317 && self.required == other.required
318 && core::ptr::eq(self.value_shape, other.value_shape)
319 && core::ptr::eq(self.field, other.field)
320 && self.category == other.category
321 }
322}
323
324impl FieldInfo {
325 pub fn key(&self) -> FieldKey<'static> {
327 match self.category {
328 FieldCategory::Flat => FieldKey::Flat(Cow::Borrowed(self.serialized_name)),
329 cat => FieldKey::Dom(cat, Cow::Borrowed(self.serialized_name)),
330 }
331 }
332}
333
334impl Eq for FieldInfo {}
335
336#[derive(Debug)]
338#[non_exhaustive]
339pub enum MatchResult {
340 Exact,
342 WithOptionalMissing(Vec<&'static str>),
344 NoMatch {
346 missing_required: Vec<&'static str>,
348 unknown: Vec<String>,
350 },
351}
352
353#[derive(Debug, Clone)]
359pub struct Resolution {
360 variant_selections: Vec<VariantSelection>,
363
364 fields: BTreeMap<FieldKey<'static>, FieldInfo>,
368
369 required_field_names: BTreeSet<&'static str>,
371
372 known_paths: BTreeSet<KeyPath>,
376
377 dom_known_paths: BTreeSet<DomKeyPath>,
380
381 catch_all_maps: BTreeMap<FieldCategory, FieldInfo>,
385}
386
387#[derive(Debug, Clone)]
389pub struct DuplicateFieldError {
390 pub field_name: &'static str,
392 pub first_path: FieldPath,
394 pub second_path: FieldPath,
396}
397
398impl fmt::Display for DuplicateFieldError {
399 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
400 write!(
401 f,
402 "duplicate field '{}': found at {} and {}",
403 self.field_name, self.first_path, self.second_path
404 )
405 }
406}
407
408impl Resolution {
409 pub const fn new() -> Self {
411 Self {
412 variant_selections: Vec::new(),
413 fields: BTreeMap::new(),
414 required_field_names: BTreeSet::new(),
415 known_paths: BTreeSet::new(),
416 dom_known_paths: BTreeSet::new(),
417 catch_all_maps: BTreeMap::new(),
418 }
419 }
420
421 pub fn set_catch_all_map(&mut self, category: FieldCategory, info: FieldInfo) {
423 self.catch_all_maps.insert(category, info);
424 }
425
426 pub fn catch_all_map(&self, category: FieldCategory) -> Option<&FieldInfo> {
428 self.catch_all_maps.get(&category)
429 }
430
431 pub fn catch_all_maps(&self) -> &BTreeMap<FieldCategory, FieldInfo> {
433 &self.catch_all_maps
434 }
435
436 pub fn add_key_path(&mut self, path: KeyPath) {
438 self.known_paths.insert(path);
439 }
440
441 pub fn add_dom_key_path(&mut self, path: DomKeyPath) {
443 self.dom_known_paths.insert(path);
444 }
445
446 pub fn add_field(&mut self, info: FieldInfo) -> Result<(), DuplicateFieldError> {
452 let key = info.key();
453 if let Some(existing) = self.fields.get(&key)
454 && existing.path != info.path
455 {
456 return Err(DuplicateFieldError {
457 field_name: info.serialized_name,
458 first_path: existing.path.clone(),
459 second_path: info.path,
460 });
461 }
462 if info.required {
463 self.required_field_names.insert(info.serialized_name);
464 }
465 self.fields.insert(key, info);
466 Ok(())
467 }
468
469 pub fn add_variant_selection(
471 &mut self,
472 path: FieldPath,
473 enum_name: &'static str,
474 variant_name: &'static str,
475 ) {
476 self.variant_selections.push(VariantSelection {
477 path,
478 enum_name,
479 variant_name,
480 });
481 }
482
483 pub fn merge(&mut self, other: &Resolution) -> Result<(), DuplicateFieldError> {
489 for (key, info) in &other.fields {
490 if let Some(existing) = self.fields.get(key)
491 && existing.path != info.path
492 {
493 return Err(DuplicateFieldError {
494 field_name: info.serialized_name,
495 first_path: existing.path.clone(),
496 second_path: info.path.clone(),
497 });
498 }
499 self.fields.insert(key.clone(), info.clone());
500 if info.required {
501 self.required_field_names.insert(info.serialized_name);
502 }
503 }
504 for vs in &other.variant_selections {
505 self.variant_selections.push(vs.clone());
506 }
507 for path in &other.known_paths {
508 self.known_paths.insert(path.clone());
509 }
510 for path in &other.dom_known_paths {
511 self.dom_known_paths.insert(path.clone());
512 }
513 for (cat, info) in &other.catch_all_maps {
515 self.catch_all_maps.insert(*cat, info.clone());
516 }
517 Ok(())
518 }
519
520 pub fn mark_all_optional(&mut self) {
523 self.required_field_names.clear();
524 for info in self.fields.values_mut() {
525 info.required = false;
526 }
527 }
528
529 pub fn matches(&self, input_fields: &BTreeSet<Cow<'_, str>>) -> MatchResult {
531 let mut missing_required = Vec::new();
532 let mut missing_optional = Vec::new();
533
534 for info in self.fields.values() {
535 if !input_fields
536 .iter()
537 .any(|k| k.as_ref() == info.serialized_name)
538 {
539 if info.required {
540 missing_required.push(info.serialized_name);
541 } else {
542 missing_optional.push(info.serialized_name);
543 }
544 }
545 }
546
547 let unknown: Vec<String> = input_fields
549 .iter()
550 .filter(|f| {
551 !self
552 .fields
553 .values()
554 .any(|info| info.serialized_name == f.as_ref())
555 })
556 .map(|s| s.to_string())
557 .collect();
558
559 if !missing_required.is_empty() || !unknown.is_empty() {
560 MatchResult::NoMatch {
561 missing_required,
562 unknown,
563 }
564 } else if missing_optional.is_empty() {
565 MatchResult::Exact
566 } else {
567 MatchResult::WithOptionalMissing(missing_optional)
568 }
569 }
570
571 pub fn describe(&self) -> String {
576 if self.variant_selections.is_empty() {
577 String::from("(no variants)")
578 } else {
579 let parts: Vec<_> = self
580 .variant_selections
581 .iter()
582 .map(|vs| format!("{}::{}", vs.enum_name, vs.variant_name))
583 .collect();
584 parts.join(" + ")
585 }
586 }
587
588 pub fn deserialization_order(&self) -> Vec<&FieldInfo> {
590 let mut fields: Vec<_> = self.fields.values().collect();
591 fields.sort_by(|a, b| {
592 b.path
594 .depth()
595 .cmp(&a.path.depth())
596 .then_with(|| a.path.cmp(&b.path))
598 });
599 fields
600 }
601
602 pub fn field(&self, key: &FieldKey<'static>) -> Option<&FieldInfo> {
606 self.fields.get(key)
607 }
608
609 pub fn field_by_key(&self, key: &FieldKey<'_>) -> Option<&FieldInfo> {
614 self.fields.iter().find_map(|(k, v)| {
615 let matches = match (k, key) {
617 (FieldKey::Flat(a), FieldKey::Flat(b)) => a.as_ref() == b.as_ref(),
618 (FieldKey::Dom(cat_a, a), FieldKey::Dom(cat_b, b)) => {
619 cat_a == cat_b && a.as_ref() == b.as_ref()
620 }
621 _ => false,
622 };
623 if matches { Some(v) } else { None }
624 })
625 }
626
627 pub fn field_by_name(&self, name: &str) -> Option<&FieldInfo> {
630 self.fields.values().find(|f| f.serialized_name == name)
632 }
633
634 pub const fn fields(&self) -> &BTreeMap<FieldKey<'static>, FieldInfo> {
636 &self.fields
637 }
638
639 pub const fn required_field_names(&self) -> &BTreeSet<&'static str> {
641 &self.required_field_names
642 }
643
644 pub fn missing_optional_fields<'a>(
649 &'a self,
650 seen_keys: &'a BTreeSet<Cow<'_, str>>,
651 ) -> impl Iterator<Item = &'a FieldInfo> {
652 self.fields.values().filter(move |info| {
653 !info.required && !seen_keys.iter().any(|k| k.as_ref() == info.serialized_name)
654 })
655 }
656
657 pub fn variant_selections(&self) -> &[VariantSelection] {
659 &self.variant_selections
660 }
661
662 pub fn child_fields(&self) -> impl Iterator<Item = &FieldInfo> {
667 self.fields.values().filter(|f| f.field.is_child())
668 }
669
670 pub fn property_fields(&self) -> impl Iterator<Item = &FieldInfo> {
675 self.fields.values().filter(|f| !f.field.is_child())
676 }
677
678 pub const fn known_paths(&self) -> &BTreeSet<KeyPath> {
680 &self.known_paths
681 }
682
683 pub fn has_key_path(&self, path: &[&str]) -> bool {
685 self.known_paths.iter().any(|known| {
686 known.len() == path.len() && known.iter().zip(path.iter()).all(|(a, b)| *a == *b)
687 })
688 }
689
690 pub fn has_dom_key_path(&self, path: &[FieldKey<'_>]) -> bool {
692 self.dom_known_paths.iter().any(|known| {
693 known.len() == path.len()
694 && known.iter().zip(path.iter()).all(|(a, b)| {
695 match (a, b) {
697 (FieldKey::Flat(sa), FieldKey::Flat(sb)) => sa.as_ref() == sb.as_ref(),
698 (FieldKey::Dom(ca, sa), FieldKey::Dom(cb, sb)) => {
699 ca == cb && sa.as_ref() == sb.as_ref()
700 }
701 _ => false,
702 }
703 })
704 })
705 }
706
707 pub const fn dom_known_paths(&self) -> &BTreeSet<DomKeyPath> {
709 &self.dom_known_paths
710 }
711}
712
713impl Default for Resolution {
714 fn default() -> Self {
715 Self::new()
716 }
717}