use indexmap::IndexMap;
use serde_json::Value;
use crate::ir::{parse_node, Arena, NodeId};
use crate::normalize::dialect::Dialect;
pub mod desugar;
pub mod dialect;
pub mod refs;
pub mod traverse;
#[derive(Debug, Clone, serde::Serialize, serde::Deserialize)]
pub struct NormalizedSchema {
pub arena: Arena,
pub root_id: NodeId,
pub defs: IndexMap<String, NodeId>,
pub dialect: Dialect,
}
#[derive(Debug, thiserror::Error)]
pub enum NormalizeError {
#[error("parse error: {0}")]
ParseError(String),
#[error("unresolved internal $ref: {0}")]
UnresolvedRef(String),
}
pub fn normalize(value: Value) -> Result<NormalizedSchema, NormalizeError> {
let mut arena = Arena::new();
let root_node = parse_node(value).map_err(|e| NormalizeError::ParseError(e.to_string()))?;
let root_id = arena.alloc(root_node);
let dialect = dialect::detect(&arena[root_id]);
let mut defs = IndexMap::new();
build_defs(&mut arena, root_id, &mut defs)?;
traverse::expand_and_dfs(&mut arena, root_id)?;
let _ref_edges = refs::resolve_refs(&mut arena, &defs)?;
let transitive_edges = refs::transitive_ref_edges(&arena, &defs);
refs::tarjan_scc(&mut arena, &transitive_edges);
let all_ids: Vec<NodeId> = arena.iter().map(|(id, _)| id).collect();
for node_id in all_ids {
desugar::desugar_type_arrays(&mut arena, node_id);
}
Ok(NormalizedSchema {
arena,
root_id,
defs,
dialect,
})
}
fn build_defs(
arena: &mut Arena,
root_id: NodeId,
defs: &mut IndexMap<String, NodeId>,
) -> Result<(), NormalizeError> {
let defs_val = arena[root_id].annotations.defs.clone();
let definitions_val = arena[root_id].annotations.definitions.clone();
if let Some(Value::Object(map)) = defs_val {
for (name, val) in map {
let child = parse_node(val).map_err(|e| NormalizeError::ParseError(e.to_string()))?;
let child_id = arena.alloc(child);
arena[child_id].json_pointer = format!("/\u{24}defs/{}", name);
arena[child_id].parent = Some(root_id);
arena[child_id].depth = 1;
arena[root_id].children.push(child_id);
defs.insert(name, child_id);
}
}
if let Some(Value::Object(map)) = definitions_val {
for (name, val) in map {
if defs.contains_key(&name) {
continue;
}
let child = parse_node(val).map_err(|e| NormalizeError::ParseError(e.to_string()))?;
let child_id = arena.alloc(child);
arena[child_id].json_pointer = format!("/definitions/{}", name);
arena[child_id].parent = Some(root_id);
arena[child_id].depth = 1;
arena[root_id].children.push(child_id);
defs.insert(name, child_id);
}
}
Ok(())
}