validator refactor progress
This commit is contained in:
220
src/database/mod.rs
Normal file
220
src/database/mod.rs
Normal file
@ -0,0 +1,220 @@
|
||||
pub mod r#enum;
|
||||
pub mod formats;
|
||||
pub mod page;
|
||||
pub mod punc;
|
||||
pub mod schema;
|
||||
pub mod r#type;
|
||||
|
||||
use crate::database::r#enum::Enum;
|
||||
use crate::database::punc::Punc;
|
||||
use crate::database::schema::Schema;
|
||||
use crate::database::r#type::Type;
|
||||
use std::collections::HashMap;
|
||||
|
||||
pub struct Database {
|
||||
pub enums: HashMap<String, Enum>,
|
||||
pub types: HashMap<String, Type>,
|
||||
pub puncs: HashMap<String, Punc>,
|
||||
pub schemas: HashMap<String, Schema>,
|
||||
pub descendants: HashMap<String, Vec<String>>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
pub fn new(val: &serde_json::Value) -> Self {
|
||||
let mut db = Self {
|
||||
enums: HashMap::new(),
|
||||
types: HashMap::new(),
|
||||
puncs: HashMap::new(),
|
||||
schemas: HashMap::new(),
|
||||
descendants: HashMap::new(),
|
||||
};
|
||||
|
||||
if let Some(arr) = val.get("enums").and_then(|v| v.as_array()) {
|
||||
for item in arr {
|
||||
if let Ok(def) = serde_json::from_value::<Enum>(item.clone()) {
|
||||
db.enums.insert(def.name.clone(), def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arr) = val.get("types").and_then(|v| v.as_array()) {
|
||||
for item in arr {
|
||||
if let Ok(def) = serde_json::from_value::<Type>(item.clone()) {
|
||||
db.types.insert(def.name.clone(), def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arr) = val.get("puncs").and_then(|v| v.as_array()) {
|
||||
for item in arr {
|
||||
if let Ok(def) = serde_json::from_value::<Punc>(item.clone()) {
|
||||
db.puncs.insert(def.name.clone(), def);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(arr) = val.get("schemas").and_then(|v| v.as_array()) {
|
||||
for (i, item) in arr.iter().enumerate() {
|
||||
if let Ok(mut schema) = serde_json::from_value::<Schema>(item.clone()) {
|
||||
let id = schema
|
||||
.obj
|
||||
.id
|
||||
.clone()
|
||||
.unwrap_or_else(|| format!("schema_{}", i));
|
||||
schema.obj.id = Some(id.clone());
|
||||
db.schemas.insert(id, schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
let _ = db.compile();
|
||||
db
|
||||
}
|
||||
|
||||
/// Organizes the graph of the database, compiling regex, format functions, and pointing schema references.
|
||||
fn compile(&mut self) -> Result<(), String> {
|
||||
self.collect_schemas();
|
||||
|
||||
// 1. Compile regex and formats sequentially
|
||||
for schema in self.schemas.values_mut() {
|
||||
schema.compile();
|
||||
}
|
||||
|
||||
// 2. Compute the Unified Semantic Graph (descendants)
|
||||
self.collect_descendents();
|
||||
|
||||
// 3. For any schema representing a Postgres table, cache its allowed subclasses
|
||||
self.compile_allowed_types();
|
||||
|
||||
// 4. Finally, securely link all string $refs into memory pointers (Arc)
|
||||
self.compile_pointers();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
||||
fn collect_schemas(&mut self) {
|
||||
let mut to_insert = Vec::new();
|
||||
for (_, type_def) in &self.types {
|
||||
for schema in &type_def.schemas {
|
||||
if let Some(id) = &schema.obj.id {
|
||||
to_insert.push((id.clone(), schema.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_, punc_def) in &self.puncs {
|
||||
for schema in &punc_def.schemas {
|
||||
if let Some(id) = &schema.obj.id {
|
||||
to_insert.push((id.clone(), schema.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (_, enum_def) in &self.enums {
|
||||
for schema in &enum_def.schemas {
|
||||
if let Some(id) = &schema.obj.id {
|
||||
to_insert.push((id.clone(), schema.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
for (id, schema) in to_insert {
|
||||
self.schemas.insert(id, schema);
|
||||
}
|
||||
}
|
||||
|
||||
fn collect_descendents(&mut self) {
|
||||
let mut direct_children: HashMap<String, Vec<String>> = HashMap::new();
|
||||
|
||||
// First pass: Find all schemas that have a $ref to another schema
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
for id in schema_ids {
|
||||
if let Some(ref_str) = self.schemas.get(&id).and_then(|s| s.obj.ref_string.clone()) {
|
||||
if self.schemas.contains_key(&ref_str) {
|
||||
direct_children.entry(ref_str).or_default().push(id.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Now compute descendants for all schemas
|
||||
let mut descendants_map: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for key in self.schemas.keys() {
|
||||
let mut descendants = Vec::new();
|
||||
let mut queue = Vec::new();
|
||||
if let Some(children) = direct_children.get(key) {
|
||||
queue.extend(children.iter().cloned());
|
||||
}
|
||||
|
||||
let mut visited = std::collections::HashSet::new();
|
||||
while let Some(child) = queue.pop() {
|
||||
if visited.insert(child.clone()) {
|
||||
descendants.push(child.clone());
|
||||
if let Some(grandchildren) = direct_children.get(&child) {
|
||||
queue.extend(grandchildren.iter().cloned());
|
||||
}
|
||||
}
|
||||
}
|
||||
descendants_map.insert(key.clone(), descendants);
|
||||
}
|
||||
self.descendants = descendants_map;
|
||||
}
|
||||
|
||||
fn compile_allowed_types(&mut self) {
|
||||
// 1. Identify which types act as bases (table-backed schemas)
|
||||
let mut entity_bases = HashMap::new();
|
||||
for type_def in self.types.values() {
|
||||
for type_schema in &type_def.schemas {
|
||||
if let Some(id) = &type_schema.obj.id {
|
||||
entity_bases.insert(id.clone(), type_def.name.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Compute compiled_allowed_types for all descendants of entity bases
|
||||
let mut allowed_types_map: HashMap<String, std::collections::HashSet<String>> = HashMap::new();
|
||||
for base_id in entity_bases.keys() {
|
||||
allowed_types_map.insert(
|
||||
base_id.clone(),
|
||||
self
|
||||
.descendants
|
||||
.get(base_id)
|
||||
.unwrap_or(&vec![])
|
||||
.iter()
|
||||
.cloned()
|
||||
.collect(),
|
||||
);
|
||||
if let Some(descendants) = self.descendants.get(base_id) {
|
||||
let set: std::collections::HashSet<String> = descendants.iter().cloned().collect();
|
||||
for desc_id in descendants {
|
||||
allowed_types_map.insert(desc_id.clone(), set.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 3. Inject types into the schemas
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
for id in schema_ids {
|
||||
if let Some(set) = allowed_types_map.get(&id) {
|
||||
if let Some(schema) = self.schemas.get_mut(&id) {
|
||||
schema.obj.compiled_allowed_types = Some(set.clone());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_pointers(&mut self) {
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
for id in schema_ids {
|
||||
let mut compiled_ref = None;
|
||||
|
||||
if let Some(schema) = self.schemas.get(&id) {
|
||||
if let Some(ref_str) = &schema.obj.ref_string {
|
||||
if let Some(target) = self.schemas.get(ref_str) {
|
||||
compiled_ref = Some(std::sync::Arc::new(target.clone()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(schema) = self.schemas.get_mut(&id) {
|
||||
schema.obj.compiled_ref = compiled_ref;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user