mod compare;
#[cfg(test)]
mod tests;
use std::fmt::{Display, Formatter, Result as FmtResult};
use serde::{Deserialize, Serialize};
use serde_json::Value;
pub use compare::compare_shapes;
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum DifferenceCode {
FieldMissing,
FieldAdded,
FieldPresenceChanged,
ShapeKindChanged,
ArrayLengthChanged,
MembershipChanged,
ProfileChanged,
VariantMissing,
VariantAdded,
VariantKindChanged,
TupleLengthChanged,
}
impl DifferenceCode {
pub const fn as_str(self) -> &'static str {
match self {
Self::FieldMissing => "field_missing",
Self::FieldAdded => "field_added",
Self::FieldPresenceChanged => "field_presence_changed",
Self::ShapeKindChanged => "shape_kind_changed",
Self::ArrayLengthChanged => "array_length_changed",
Self::MembershipChanged => "membership_changed",
Self::ProfileChanged => "profile_changed",
Self::VariantMissing => "variant_missing",
Self::VariantAdded => "variant_added",
Self::VariantKindChanged => "variant_kind_changed",
Self::TupleLengthChanged => "tuple_length_changed",
}
}
}
impl Display for DifferenceCode {
fn fmt(&self, formatter: &mut Formatter<'_>) -> FmtResult {
formatter.write_str(self.as_str())
}
}
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(tag = "kind", rename_all = "snake_case", deny_unknown_fields)]
pub enum PathSegment {
Field {
name: String,
},
OptionValue,
SequenceItem,
ArrayItem,
MapValue,
SetItem,
Variant {
name: String,
},
TupleItem {
index: usize,
},
VariantValue,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(deny_unknown_fields)]
pub struct SchemaDifference {
pub code: DifferenceCode,
pub path: Vec<PathSegment>,
pub expected: Value,
pub actual: Value,
pub hint: String,
}
pub fn readable_path(path: &[PathSegment]) -> String {
let mut rendered = "$".to_owned();
for segment in path {
match segment {
PathSegment::Field { name } => {
rendered.push_str(&format!("[{}]", json_name(name)));
}
PathSegment::Variant { name } => {
rendered.push_str(&format!(".variant[{}]", json_name(name)));
}
PathSegment::OptionValue => rendered.push_str(".option_value"),
PathSegment::SequenceItem => rendered.push_str(".sequence_item"),
PathSegment::ArrayItem => rendered.push_str(".array_item"),
PathSegment::MapValue => rendered.push_str(".map_value"),
PathSegment::SetItem => rendered.push_str(".set_item"),
PathSegment::TupleItem { index } => rendered.push_str(&format!("[{index}]")),
PathSegment::VariantValue => rendered.push_str(".variant_value"),
}
}
rendered
}
fn json_name(name: &str) -> String {
serde_json::to_string(name).expect("wire names serialize as JSON strings")
}