use indexmap::{IndexMap, IndexSet};
use oas3::spec::{ObjectOrReference, ObjectSchema, Ref, Schema};
pub fn parse_schema_ref_path(ref_path: &str) -> Option<String> {
if !ref_path.starts_with("#/components") {
return None;
}
match ref_path.parse::<Ref>() {
Ok(component) => Some(component.name),
Err(_) => None,
}
}
pub trait SchemaRefName {
fn schema_ref_name(&self) -> Option<String>;
}
impl SchemaRefName for Schema {
fn schema_ref_name(&self) -> Option<String> {
match self {
Schema::Object(schema_ref) => schema_ref.schema_ref_name(),
Schema::Boolean(_) => None,
}
}
}
impl SchemaRefName for ObjectOrReference<ObjectSchema> {
fn schema_ref_name(&self) -> Option<String> {
match self {
ObjectOrReference::Ref { ref_path, .. } => parse_schema_ref_path(ref_path),
ObjectOrReference::Object(_) => None,
}
}
}
pub trait SchemaInspect {
fn as_inline(&self) -> Option<&ObjectSchema>;
fn ref_path(&self) -> Option<&str>;
}
impl SchemaInspect for Schema {
fn as_inline(&self) -> Option<&ObjectSchema> {
let Schema::Object(obj_ref) = self else {
return None;
};
match obj_ref.as_ref() {
ObjectOrReference::Object(schema) => Some(schema),
ObjectOrReference::Ref { .. } => None,
}
}
fn ref_path(&self) -> Option<&str> {
let Schema::Object(obj_ref) = self else {
return None;
};
match obj_ref.as_ref() {
ObjectOrReference::Ref { ref_path, .. } => Some(ref_path),
ObjectOrReference::Object(_) => None,
}
}
}
impl SchemaInspect for ObjectOrReference<ObjectSchema> {
fn as_inline(&self) -> Option<&ObjectSchema> {
match self {
ObjectOrReference::Object(schema) => Some(schema),
ObjectOrReference::Ref { .. } => None,
}
}
fn ref_path(&self) -> Option<&str> {
match self {
ObjectOrReference::Ref { ref_path, .. } => Some(ref_path),
ObjectOrReference::Object(_) => None,
}
}
}
pub fn extract_union_fingerprint<T: SchemaRefName>(variants: &[T]) -> UnionFingerprint {
variants
.iter()
.filter_map(SchemaRefName::schema_ref_name)
.collect::<IndexSet<_>>()
.into_iter()
.collect()
}
pub type SchemaMap = IndexMap<String, ObjectSchema>;
pub type SchemaSet = IndexSet<String>;
pub type UnionFingerprint = Vec<String>;
pub type UnionFingerprints = IndexMap<UnionFingerprint, String>;
pub fn build_union_fingerprints(schemas: &SchemaMap) -> UnionFingerprints {
let mut fingerprints = UnionFingerprints::new();
for (name, schema) in schemas {
for variants in [&schema.one_of, &schema.any_of] {
let refs = extract_union_fingerprint(variants);
if refs.len() >= 2 {
fingerprints.insert(refs, name.clone());
}
}
}
fingerprints
}