use serde_json::{Number, Value};
use crate::{
canonical::{
ir::{
BoundCardinality, BoundInteger, CanonicalJson, IntegerBounds, SchemaKind, StringLeaf,
},
CanonicalSchema,
},
JsonType, JsonTypeSet,
};
pub use crate::canonical::ir::CanonicalKind;
impl CanonicalKind {
#[must_use]
pub fn as_str(self) -> &'static str {
self.into()
}
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq)]
pub enum CanonicalView {
MultiType(JsonTypeSet),
TypedGroup(TypedGroupView),
String(StringView),
Integer(IntegerView),
Const(Value),
Enum(Vec<Value>),
AnyOf(Vec<CanonicalSchema>),
True,
False,
Raw(Value),
}
#[derive(Debug, Clone, PartialEq)]
pub struct TypedGroupView {
pub ty: JsonType,
pub body: CanonicalSchema,
}
#[derive(Debug, Clone, PartialEq)]
pub struct StringView {
pub min_length: Option<Number>,
pub max_length: Option<Number>,
pub patterns: Vec<String>,
}
#[derive(Debug, Clone, PartialEq)]
pub struct IntegerView {
pub minimum: Option<Number>,
pub maximum: Option<Number>,
}
impl CanonicalSchema {
#[must_use]
pub fn view(&self) -> CanonicalView {
match self.schema_kind() {
SchemaKind::MultiType(set) => CanonicalView::MultiType(*set),
SchemaKind::TypedGroup { ty, body } => CanonicalView::TypedGroup(TypedGroupView {
ty: *ty,
body: self.wrap_child(body),
}),
SchemaKind::String(leaf) => CanonicalView::String(string_view(leaf)),
SchemaKind::Integer(bounds) => CanonicalView::Integer(integer_view(bounds)),
SchemaKind::Const(value) => CanonicalView::Const(value.to_value()),
SchemaKind::Enum(values) => {
CanonicalView::Enum(values.iter().map(CanonicalJson::to_value).collect())
}
SchemaKind::AnyOf(branches) => CanonicalView::AnyOf(
branches
.iter()
.map(|branch| self.wrap_child(branch))
.collect(),
),
SchemaKind::True => CanonicalView::True,
SchemaKind::False => CanonicalView::False,
SchemaKind::Raw(_) => CanonicalView::Raw(self.to_json_schema()),
}
}
#[must_use]
pub fn kind(&self) -> CanonicalKind {
self.schema_kind().into()
}
}
fn integer_view(bounds: &IntegerBounds) -> IntegerView {
IntegerView {
minimum: bounds.minimum.as_ref().map(BoundInteger::to_number),
maximum: bounds.maximum.as_ref().map(BoundInteger::to_number),
}
}
fn string_view(leaf: &StringLeaf) -> StringView {
StringView {
min_length: leaf
.lengths
.minimum
.as_ref()
.map(BoundCardinality::to_number),
max_length: leaf
.lengths
.maximum
.as_ref()
.map(BoundCardinality::to_number),
patterns: leaf.patterns.iter().map(ToString::to_string).collect(),
}
}