extern crate alloc;
use alloc::borrow::Cow;
use alloc::collections::BTreeMap;
use alloc::collections::BTreeSet;
use alloc::format;
use alloc::string::String;
use alloc::string::ToString;
use alloc::vec::Vec;
use core::fmt;
use facet_core::{Field, Shape};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd, Default)]
pub enum FieldCategory {
#[default]
Flat,
Attribute,
Element,
Text,
Tag,
Elements,
}
impl FieldCategory {
pub fn from_field_dom(field: &Field) -> Option<Self> {
if field.is_flattened() {
return None;
}
if field.is_attribute() {
Some(FieldCategory::Attribute)
} else if field.is_text() {
Some(FieldCategory::Text)
} else if field.is_tag() {
Some(FieldCategory::Tag)
} else if field.is_elements() {
Some(FieldCategory::Elements)
} else {
Some(FieldCategory::Element)
}
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum FieldKey<'a> {
Flat(Cow<'a, str>),
Dom(FieldCategory, Cow<'a, str>),
}
impl<'a> FieldKey<'a> {
pub fn flat(name: impl Into<Cow<'a, str>>) -> Self {
FieldKey::Flat(name.into())
}
pub fn attribute(name: impl Into<Cow<'a, str>>) -> Self {
FieldKey::Dom(FieldCategory::Attribute, name.into())
}
pub fn element(name: impl Into<Cow<'a, str>>) -> Self {
FieldKey::Dom(FieldCategory::Element, name.into())
}
pub fn text() -> Self {
FieldKey::Dom(FieldCategory::Text, Cow::Borrowed(""))
}
pub fn tag() -> Self {
FieldKey::Dom(FieldCategory::Tag, Cow::Borrowed(""))
}
pub fn elements() -> Self {
FieldKey::Dom(FieldCategory::Elements, Cow::Borrowed(""))
}
pub fn name(&self) -> &str {
match self {
FieldKey::Flat(name) => name.as_ref(),
FieldKey::Dom(_, name) => name.as_ref(),
}
}
pub fn category(&self) -> Option<FieldCategory> {
match self {
FieldKey::Flat(_) => None,
FieldKey::Dom(cat, _) => Some(*cat),
}
}
pub fn into_owned(self) -> FieldKey<'static> {
match self {
FieldKey::Flat(name) => FieldKey::Flat(Cow::Owned(name.into_owned())),
FieldKey::Dom(cat, name) => FieldKey::Dom(cat, Cow::Owned(name.into_owned())),
}
}
}
impl<'a> From<&'a str> for FieldKey<'a> {
fn from(s: &'a str) -> Self {
FieldKey::Flat(Cow::Borrowed(s))
}
}
impl From<String> for FieldKey<'static> {
fn from(s: String) -> Self {
FieldKey::Flat(Cow::Owned(s))
}
}
impl<'a> From<&'a String> for FieldKey<'a> {
fn from(s: &'a String) -> Self {
FieldKey::Flat(Cow::Borrowed(s.as_str()))
}
}
impl<'a> From<Cow<'a, str>> for FieldKey<'a> {
fn from(s: Cow<'a, str>) -> Self {
FieldKey::Flat(s)
}
}
impl fmt::Display for FieldKey<'_> {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
FieldKey::Flat(name) => write!(f, "{}", name),
FieldKey::Dom(cat, name) => write!(f, "{:?}:{}", cat, name),
}
}
}
pub type KeyPath = Vec<&'static str>;
pub type DomKeyPath = Vec<FieldKey<'static>>;
#[derive(Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub enum PathSegment {
Field(&'static str),
Variant(&'static str, &'static str),
}
#[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct FieldPath {
segments: Vec<PathSegment>,
}
impl fmt::Debug for FieldPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "FieldPath(")?;
for (i, seg) in self.segments.iter().enumerate() {
if i > 0 {
write!(f, ".")?;
}
match seg {
PathSegment::Field(name) => write!(f, "{name}")?,
PathSegment::Variant(field, variant) => write!(f, "{field}::{variant}")?,
}
}
write!(f, ")")
}
}
impl fmt::Display for FieldPath {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
let mut first = true;
for seg in &self.segments {
match seg {
PathSegment::Field(name) => {
if !first {
write!(f, ".")?;
}
write!(f, "{name}")?;
first = false;
}
PathSegment::Variant(_, _) => {
}
}
}
Ok(())
}
}
impl FieldPath {
pub const fn empty() -> Self {
Self {
segments: Vec::new(),
}
}
pub const fn depth(&self) -> usize {
self.segments.len()
}
pub fn push_field(&self, name: &'static str) -> Self {
let mut new = self.clone();
new.segments.push(PathSegment::Field(name));
new
}
pub fn push_variant(&self, field_name: &'static str, variant_name: &'static str) -> Self {
let mut new = self.clone();
new.segments
.push(PathSegment::Variant(field_name, variant_name));
new
}
pub fn parent(&self) -> Self {
let mut new = self.clone();
new.segments.pop();
new
}
pub fn segments(&self) -> &[PathSegment] {
&self.segments
}
pub fn last(&self) -> Option<&PathSegment> {
self.segments.last()
}
}
#[derive(Debug, Clone)]
pub struct VariantSelection {
pub path: FieldPath,
pub enum_name: &'static str,
pub variant_name: &'static str,
}
#[derive(Debug, Clone)]
pub struct FieldInfo {
pub serialized_name: &'static str,
pub path: FieldPath,
pub required: bool,
pub value_shape: &'static Shape,
pub field: &'static Field,
pub category: FieldCategory,
}
impl PartialEq for FieldInfo {
fn eq(&self, other: &Self) -> bool {
self.serialized_name == other.serialized_name
&& self.path == other.path
&& self.required == other.required
&& core::ptr::eq(self.value_shape, other.value_shape)
&& core::ptr::eq(self.field, other.field)
&& self.category == other.category
}
}
impl FieldInfo {
pub fn key(&self) -> FieldKey<'static> {
match self.category {
FieldCategory::Flat => FieldKey::Flat(Cow::Borrowed(self.serialized_name)),
cat => FieldKey::Dom(cat, Cow::Borrowed(self.serialized_name)),
}
}
}
impl Eq for FieldInfo {}
#[derive(Debug)]
pub enum MatchResult {
Exact,
WithOptionalMissing(Vec<&'static str>),
NoMatch {
missing_required: Vec<&'static str>,
unknown: Vec<String>,
},
}
#[derive(Debug, Clone)]
pub struct Resolution {
variant_selections: Vec<VariantSelection>,
fields: BTreeMap<FieldKey<'static>, FieldInfo>,
required_field_names: BTreeSet<&'static str>,
known_paths: BTreeSet<KeyPath>,
dom_known_paths: BTreeSet<DomKeyPath>,
catch_all_maps: BTreeMap<FieldCategory, FieldInfo>,
}
#[derive(Debug, Clone)]
pub struct DuplicateFieldError {
pub field_name: &'static str,
pub first_path: FieldPath,
pub second_path: FieldPath,
}
impl fmt::Display for DuplicateFieldError {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"duplicate field '{}': found at {} and {}",
self.field_name, self.first_path, self.second_path
)
}
}
impl Resolution {
pub const fn new() -> Self {
Self {
variant_selections: Vec::new(),
fields: BTreeMap::new(),
required_field_names: BTreeSet::new(),
known_paths: BTreeSet::new(),
dom_known_paths: BTreeSet::new(),
catch_all_maps: BTreeMap::new(),
}
}
pub fn set_catch_all_map(&mut self, category: FieldCategory, info: FieldInfo) {
self.catch_all_maps.insert(category, info);
}
pub fn catch_all_map(&self, category: FieldCategory) -> Option<&FieldInfo> {
self.catch_all_maps.get(&category)
}
pub fn catch_all_maps(&self) -> &BTreeMap<FieldCategory, FieldInfo> {
&self.catch_all_maps
}
pub fn add_key_path(&mut self, path: KeyPath) {
self.known_paths.insert(path);
}
pub fn add_dom_key_path(&mut self, path: DomKeyPath) {
self.dom_known_paths.insert(path);
}
pub fn add_field(&mut self, info: FieldInfo) -> Result<(), DuplicateFieldError> {
let key = info.key();
if let Some(existing) = self.fields.get(&key)
&& existing.path != info.path
{
return Err(DuplicateFieldError {
field_name: info.serialized_name,
first_path: existing.path.clone(),
second_path: info.path,
});
}
if info.required {
self.required_field_names.insert(info.serialized_name);
}
self.fields.insert(key, info);
Ok(())
}
pub fn add_variant_selection(
&mut self,
path: FieldPath,
enum_name: &'static str,
variant_name: &'static str,
) {
self.variant_selections.push(VariantSelection {
path,
enum_name,
variant_name,
});
}
pub fn merge(&mut self, other: &Resolution) -> Result<(), DuplicateFieldError> {
for (key, info) in &other.fields {
if let Some(existing) = self.fields.get(key)
&& existing.path != info.path
{
return Err(DuplicateFieldError {
field_name: info.serialized_name,
first_path: existing.path.clone(),
second_path: info.path.clone(),
});
}
self.fields.insert(key.clone(), info.clone());
if info.required {
self.required_field_names.insert(info.serialized_name);
}
}
for vs in &other.variant_selections {
self.variant_selections.push(vs.clone());
}
for path in &other.known_paths {
self.known_paths.insert(path.clone());
}
for path in &other.dom_known_paths {
self.dom_known_paths.insert(path.clone());
}
for (cat, info) in &other.catch_all_maps {
self.catch_all_maps.insert(*cat, info.clone());
}
Ok(())
}
pub fn mark_all_optional(&mut self) {
self.required_field_names.clear();
for info in self.fields.values_mut() {
info.required = false;
}
}
pub fn matches(&self, input_fields: &BTreeSet<Cow<'_, str>>) -> MatchResult {
let mut missing_required = Vec::new();
let mut missing_optional = Vec::new();
for info in self.fields.values() {
if !input_fields
.iter()
.any(|k| k.as_ref() == info.serialized_name)
{
if info.required {
missing_required.push(info.serialized_name);
} else {
missing_optional.push(info.serialized_name);
}
}
}
let unknown: Vec<String> = input_fields
.iter()
.filter(|f| {
!self
.fields
.values()
.any(|info| info.serialized_name == f.as_ref())
})
.map(|s| s.to_string())
.collect();
if !missing_required.is_empty() || !unknown.is_empty() {
MatchResult::NoMatch {
missing_required,
unknown,
}
} else if missing_optional.is_empty() {
MatchResult::Exact
} else {
MatchResult::WithOptionalMissing(missing_optional)
}
}
pub fn describe(&self) -> String {
if self.variant_selections.is_empty() {
String::from("(no variants)")
} else {
let parts: Vec<_> = self
.variant_selections
.iter()
.map(|vs| format!("{}::{}", vs.enum_name, vs.variant_name))
.collect();
parts.join(" + ")
}
}
pub fn deserialization_order(&self) -> Vec<&FieldInfo> {
let mut fields: Vec<_> = self.fields.values().collect();
fields.sort_by(|a, b| {
b.path
.depth()
.cmp(&a.path.depth())
.then_with(|| a.path.cmp(&b.path))
});
fields
}
pub fn field(&self, key: &FieldKey<'static>) -> Option<&FieldInfo> {
self.fields.get(key)
}
pub fn field_by_key(&self, key: &FieldKey<'_>) -> Option<&FieldInfo> {
self.fields.iter().find_map(|(k, v)| {
let matches = match (k, key) {
(FieldKey::Flat(a), FieldKey::Flat(b)) => a.as_ref() == b.as_ref(),
(FieldKey::Dom(cat_a, a), FieldKey::Dom(cat_b, b)) => {
cat_a == cat_b && a.as_ref() == b.as_ref()
}
_ => false,
};
if matches { Some(v) } else { None }
})
}
pub fn field_by_name(&self, name: &str) -> Option<&FieldInfo> {
self.fields.values().find(|f| f.serialized_name == name)
}
pub const fn fields(&self) -> &BTreeMap<FieldKey<'static>, FieldInfo> {
&self.fields
}
pub const fn required_field_names(&self) -> &BTreeSet<&'static str> {
&self.required_field_names
}
pub fn missing_optional_fields<'a>(
&'a self,
seen_keys: &'a BTreeSet<Cow<'_, str>>,
) -> impl Iterator<Item = &'a FieldInfo> {
self.fields.values().filter(move |info| {
!info.required && !seen_keys.iter().any(|k| k.as_ref() == info.serialized_name)
})
}
pub fn variant_selections(&self) -> &[VariantSelection] {
&self.variant_selections
}
pub fn child_fields(&self) -> impl Iterator<Item = &FieldInfo> {
self.fields.values().filter(|f| f.field.is_child())
}
pub fn property_fields(&self) -> impl Iterator<Item = &FieldInfo> {
self.fields.values().filter(|f| !f.field.is_child())
}
pub const fn known_paths(&self) -> &BTreeSet<KeyPath> {
&self.known_paths
}
pub fn has_key_path(&self, path: &[&str]) -> bool {
self.known_paths.iter().any(|known| {
known.len() == path.len() && known.iter().zip(path.iter()).all(|(a, b)| *a == *b)
})
}
pub fn has_dom_key_path(&self, path: &[FieldKey<'_>]) -> bool {
self.dom_known_paths.iter().any(|known| {
known.len() == path.len()
&& known.iter().zip(path.iter()).all(|(a, b)| {
match (a, b) {
(FieldKey::Flat(sa), FieldKey::Flat(sb)) => sa.as_ref() == sb.as_ref(),
(FieldKey::Dom(ca, sa), FieldKey::Dom(cb, sb)) => {
ca == cb && sa.as_ref() == sb.as_ref()
}
_ => false,
}
})
})
}
pub const fn dom_known_paths(&self) -> &BTreeSet<DomKeyPath> {
&self.dom_known_paths
}
}
impl Default for Resolution {
fn default() -> Self {
Self::new()
}
}