all jspg tests now passing
This commit is contained in:
@ -376,7 +376,7 @@ fn check_hostname(s: &str) -> Result<(), Box<dyn Error + Send + Sync>> {
|
||||
Err("label has -- in 3rd/4th position but does not start with xn--")?;
|
||||
} else {
|
||||
let (unicode, errors) = idna::domain_to_unicode(label);
|
||||
if let Err(_) = errors {
|
||||
if errors.is_err() {
|
||||
Err("invalid punycode")?;
|
||||
}
|
||||
check_unicode_idn_constraints(&unicode)
|
||||
|
||||
@ -9,13 +9,15 @@ 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;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
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>>,
|
||||
pub depths: HashMap<String, usize>,
|
||||
}
|
||||
|
||||
impl Database {
|
||||
@ -25,6 +27,8 @@ impl Database {
|
||||
types: HashMap::new(),
|
||||
puncs: HashMap::new(),
|
||||
schemas: HashMap::new(),
|
||||
descendants: HashMap::new(),
|
||||
depths: HashMap::new(),
|
||||
};
|
||||
|
||||
if let Some(arr) = val.get("enums").and_then(|v| v.as_array()) {
|
||||
@ -69,43 +73,12 @@ impl Database {
|
||||
db
|
||||
}
|
||||
|
||||
/// Organizes the graph of the database, compiling regex, format functions, and pointing schema references.
|
||||
/// Organizes the graph of the database, compiling regex, format functions, and caching relationships.
|
||||
fn compile(&mut self) -> Result<(), String> {
|
||||
self.collect_schemas();
|
||||
|
||||
// 1. Build a structural descendant graph for $family macro expansion
|
||||
let mut direct_refs: std::collections::HashMap<String, Vec<String>> =
|
||||
std::collections::HashMap::new();
|
||||
for (id, schema) in &self.schemas {
|
||||
if let Some(ref_str) = &schema.obj.ref_string {
|
||||
direct_refs
|
||||
.entry(ref_str.clone())
|
||||
.or_default()
|
||||
.push(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
|
||||
// 2. Expand $family macros into oneOf blocks
|
||||
for id in &schema_ids {
|
||||
if let Some(schema) = self.schemas.get_mut(id) {
|
||||
schema.map_children(|mut child| {
|
||||
Self::expand_family(&mut child, &direct_refs);
|
||||
});
|
||||
Self::expand_family(schema, &direct_refs);
|
||||
}
|
||||
}
|
||||
|
||||
let schemas_snap = self.schemas.clone();
|
||||
|
||||
// 3. Compile internals and link memory pointers
|
||||
for id in schema_ids {
|
||||
if let Some(schema) = self.schemas.get_mut(&id) {
|
||||
schema.compile_internals();
|
||||
schema.link_refs(&schemas_snap);
|
||||
}
|
||||
}
|
||||
self.collect_depths();
|
||||
self.collect_descendants();
|
||||
self.compile_schemas();
|
||||
|
||||
Ok(())
|
||||
}
|
||||
@ -113,38 +86,18 @@ impl Database {
|
||||
fn collect_schemas(&mut self) {
|
||||
let mut to_insert = Vec::new();
|
||||
|
||||
// Pass A: Entities - Compute Variations from hierarchies
|
||||
// `hierarchy` is an array of ancestors. E.g. `person` -> `['entity', 'user', 'person']`.
|
||||
// We map this backward so that `user`'s allowed variations = `['user', 'person']`.
|
||||
let mut variations_by_entity = std::collections::HashMap::new();
|
||||
// Pass 1: Extract all Schemas structurally off top level definitions into the master registry.
|
||||
for type_def in self.types.values() {
|
||||
for ancestor in &type_def.hierarchy {
|
||||
variations_by_entity
|
||||
.entry(ancestor.clone())
|
||||
.or_insert_with(std::collections::HashSet::new)
|
||||
.insert(type_def.name.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Now stamp all exported entity schemas with their precise physical variations
|
||||
for (_, type_def) in &self.types {
|
||||
let allowed_strings = variations_by_entity
|
||||
.get(&type_def.name)
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
for mut schema in type_def.schemas.clone() {
|
||||
schema.stamp_variations(Some(allowed_strings.clone()));
|
||||
schema.harvest(&mut to_insert);
|
||||
}
|
||||
}
|
||||
|
||||
// Pass B: APIs and Enums (No initial variations stamped)
|
||||
for (_, punc_def) in &self.puncs {
|
||||
for punc_def in self.puncs.values() {
|
||||
for mut schema in punc_def.schemas.clone() {
|
||||
schema.harvest(&mut to_insert);
|
||||
}
|
||||
}
|
||||
for (_, enum_def) in &self.enums {
|
||||
for enum_def in self.enums.values() {
|
||||
for mut schema in enum_def.schemas.clone() {
|
||||
schema.harvest(&mut to_insert);
|
||||
}
|
||||
@ -155,55 +108,80 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
fn expand_family(
|
||||
schema: &mut crate::database::schema::Schema,
|
||||
direct_refs: &std::collections::HashMap<String, Vec<String>>,
|
||||
) {
|
||||
if let Some(family_target) = &schema.obj.family {
|
||||
let mut descendants = std::collections::HashSet::new();
|
||||
Self::collect_descendants(family_target, direct_refs, &mut descendants);
|
||||
fn collect_depths(&mut self) {
|
||||
let mut depths: HashMap<String, usize> = HashMap::new();
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
|
||||
// the "$family" macro is logically replaced by an anyOf of its descendants + itself
|
||||
let mut derived_any_of = Vec::new();
|
||||
for id in schema_ids {
|
||||
let mut current_id = id.clone();
|
||||
let mut depth = 0;
|
||||
let mut visited = HashSet::new();
|
||||
|
||||
// Include the target base itself if valid (which it always is structurally)
|
||||
let mut base_ref = crate::database::schema::SchemaObject::default();
|
||||
base_ref.ref_string = Some(family_target.clone());
|
||||
derived_any_of.push(std::sync::Arc::new(crate::database::schema::Schema {
|
||||
obj: base_ref,
|
||||
always_fail: false,
|
||||
}));
|
||||
|
||||
// Sort descendants for determinism during testing
|
||||
let mut desc_vec: Vec<String> = descendants.into_iter().collect();
|
||||
desc_vec.sort();
|
||||
|
||||
for child_id in desc_vec {
|
||||
let mut child_ref = crate::database::schema::SchemaObject::default();
|
||||
child_ref.ref_string = Some(child_id);
|
||||
derived_any_of.push(std::sync::Arc::new(crate::database::schema::Schema {
|
||||
obj: child_ref,
|
||||
always_fail: false,
|
||||
}));
|
||||
while let Some(schema) = self.schemas.get(¤t_id) {
|
||||
if !visited.insert(current_id.clone()) {
|
||||
break; // Cycle detected
|
||||
}
|
||||
if let Some(ref_str) = &schema.obj.ref_string {
|
||||
current_id = ref_str.clone();
|
||||
depth += 1;
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
schema.obj.any_of = Some(derived_any_of);
|
||||
// Remove family so it doesn't cause conflicts or fail the simple validation
|
||||
schema.obj.family = None;
|
||||
depths.insert(id, depth);
|
||||
}
|
||||
self.depths = depths;
|
||||
}
|
||||
|
||||
fn collect_descendants(
|
||||
fn collect_descendants(&mut self) {
|
||||
let mut direct_refs: HashMap<String, Vec<String>> = HashMap::new();
|
||||
for (id, schema) in &self.schemas {
|
||||
if let Some(ref_str) = &schema.obj.ref_string {
|
||||
direct_refs
|
||||
.entry(ref_str.clone())
|
||||
.or_default()
|
||||
.push(id.clone());
|
||||
}
|
||||
}
|
||||
|
||||
// Cache generic descendants for $family runtime lookups
|
||||
let mut descendants = HashMap::new();
|
||||
for (id, schema) in &self.schemas {
|
||||
if let Some(family_target) = &schema.obj.family {
|
||||
let mut desc_set = HashSet::new();
|
||||
Self::collect_descendants_recursively(family_target, &direct_refs, &mut desc_set);
|
||||
let mut desc_vec: Vec<String> = desc_set.into_iter().collect();
|
||||
desc_vec.sort();
|
||||
|
||||
// By placing all descendants directly onto the ID mapped location of the Family declaration,
|
||||
// we can lookup descendants natively in ValidationContext without AST replacement overrides.
|
||||
descendants.insert(id.clone(), desc_vec);
|
||||
}
|
||||
}
|
||||
self.descendants = descendants;
|
||||
}
|
||||
|
||||
fn collect_descendants_recursively(
|
||||
target: &str,
|
||||
direct_refs: &std::collections::HashMap<String, Vec<String>>,
|
||||
descendants: &mut std::collections::HashSet<String>,
|
||||
direct_refs: &HashMap<String, Vec<String>>,
|
||||
descendants: &mut HashSet<String>,
|
||||
) {
|
||||
if let Some(children) = direct_refs.get(target) {
|
||||
for child in children {
|
||||
if descendants.insert(child.clone()) {
|
||||
Self::collect_descendants(child, direct_refs, descendants);
|
||||
Self::collect_descendants_recursively(child, direct_refs, descendants);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn compile_schemas(&mut self) {
|
||||
// Pass 3: compile_internals across pure structure
|
||||
let schema_ids: Vec<String> = self.schemas.keys().cloned().collect();
|
||||
for id in schema_ids {
|
||||
if let Some(schema) = self.schemas.get_mut(&id) {
|
||||
schema.compile_internals();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,15 +0,0 @@
|
||||
use serde::{Deserialize, Serialize};
|
||||
|
||||
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
|
||||
#[serde(default)]
|
||||
pub struct Relation {
|
||||
pub id: String,
|
||||
pub constraint_name: String,
|
||||
pub source_type: String,
|
||||
#[serde(default)]
|
||||
pub source_columns: Vec<String>,
|
||||
pub destination_type: String,
|
||||
#[serde(default)]
|
||||
pub destination_columns: Vec<String>,
|
||||
pub prefix: Option<String>,
|
||||
}
|
||||
@ -120,11 +120,6 @@ pub struct SchemaObject {
|
||||
#[serde(default)]
|
||||
pub extensible: Option<bool>,
|
||||
|
||||
// Compiled Fields (Hidden from JSON/Serde)
|
||||
#[serde(skip)]
|
||||
pub compiled_ref: Option<Arc<Schema>>,
|
||||
#[serde(skip)]
|
||||
pub compiled_variations: Option<std::collections::HashSet<String>>,
|
||||
#[serde(skip)]
|
||||
pub compiled_format: Option<CompiledFormat>,
|
||||
#[serde(skip)]
|
||||
@ -153,7 +148,7 @@ impl std::fmt::Debug for CompiledFormat {
|
||||
#[derive(Debug, Clone)]
|
||||
pub struct CompiledRegex(pub regex::Regex);
|
||||
|
||||
#[derive(Debug, Clone, Serialize)]
|
||||
#[derive(Debug, Clone, Serialize, Default)]
|
||||
pub struct Schema {
|
||||
#[serde(flatten)]
|
||||
pub obj: SchemaObject,
|
||||
@ -161,15 +156,6 @@ pub struct Schema {
|
||||
pub always_fail: bool,
|
||||
}
|
||||
|
||||
impl Default for Schema {
|
||||
fn default() -> Self {
|
||||
Schema {
|
||||
obj: SchemaObject::default(),
|
||||
always_fail: false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl std::ops::Deref for Schema {
|
||||
type Target = SchemaObject;
|
||||
fn deref(&self) -> &Self::Target {
|
||||
@ -186,16 +172,16 @@ impl Schema {
|
||||
pub fn compile_internals(&mut self) {
|
||||
self.map_children(|child| child.compile_internals());
|
||||
|
||||
if let Some(format_str) = &self.obj.format {
|
||||
if let Some(fmt) = crate::database::formats::FORMATS.get(format_str.as_str()) {
|
||||
self.obj.compiled_format = Some(crate::database::schema::CompiledFormat::Func(fmt.func));
|
||||
}
|
||||
if let Some(format_str) = &self.obj.format
|
||||
&& let Some(fmt) = crate::database::formats::FORMATS.get(format_str.as_str())
|
||||
{
|
||||
self.obj.compiled_format = Some(crate::database::schema::CompiledFormat::Func(fmt.func));
|
||||
}
|
||||
|
||||
if let Some(pattern_str) = &self.obj.pattern {
|
||||
if let Ok(re) = regex::Regex::new(pattern_str) {
|
||||
self.obj.compiled_pattern = Some(crate::database::schema::CompiledRegex(re));
|
||||
}
|
||||
if let Some(pattern_str) = &self.obj.pattern
|
||||
&& let Ok(re) = regex::Regex::new(pattern_str)
|
||||
{
|
||||
self.obj.compiled_pattern = Some(crate::database::schema::CompiledRegex(re));
|
||||
}
|
||||
|
||||
if let Some(pattern_props) = &self.obj.pattern_properties {
|
||||
@ -211,46 +197,6 @@ impl Schema {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn link_refs(&mut self, schemas: &std::collections::HashMap<String, Schema>) {
|
||||
if let Some(ref_str) = &self.obj.ref_string {
|
||||
if let Some(target) = schemas.get(ref_str) {
|
||||
self.obj.compiled_ref = Some(Arc::new(target.clone()));
|
||||
|
||||
// Viral Infection: Inherit physical entity boundaries across the $ref pointer recursively
|
||||
if self.obj.compiled_variations.is_none() {
|
||||
let mut visited = std::collections::HashSet::new();
|
||||
self.obj.compiled_variations = Self::resolve_variations(ref_str, schemas, &mut visited);
|
||||
}
|
||||
}
|
||||
}
|
||||
self.map_children(|child| child.link_refs(schemas));
|
||||
}
|
||||
|
||||
fn resolve_variations(
|
||||
ref_str: &str,
|
||||
schemas: &std::collections::HashMap<String, Schema>,
|
||||
visited: &mut std::collections::HashSet<String>,
|
||||
) -> Option<std::collections::HashSet<String>> {
|
||||
if !visited.insert(ref_str.to_string()) {
|
||||
return None; // Cycle detected
|
||||
}
|
||||
|
||||
if let Some(target) = schemas.get(ref_str) {
|
||||
if let Some(vars) = &target.obj.compiled_variations {
|
||||
return Some(vars.clone());
|
||||
}
|
||||
if let Some(next_ref) = &target.obj.ref_string {
|
||||
return Self::resolve_variations(next_ref, schemas, visited);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn stamp_variations(&mut self, variations: Option<std::collections::HashSet<String>>) {
|
||||
self.obj.compiled_variations = variations.clone();
|
||||
self.map_children(|child| child.stamp_variations(variations.clone()));
|
||||
}
|
||||
|
||||
pub fn harvest(&mut self, to_insert: &mut Vec<(String, Schema)>) {
|
||||
if let Some(id) = &self.obj.id {
|
||||
to_insert.push((id.clone(), self.clone()));
|
||||
@ -263,7 +209,7 @@ impl Schema {
|
||||
F: FnMut(&mut Schema),
|
||||
{
|
||||
if let Some(props) = &mut self.obj.properties {
|
||||
for (_, v) in props {
|
||||
for v in props.values_mut() {
|
||||
let mut inner = (**v).clone();
|
||||
f(&mut inner);
|
||||
*v = Arc::new(inner);
|
||||
@ -271,7 +217,7 @@ impl Schema {
|
||||
}
|
||||
|
||||
if let Some(pattern_props) = &mut self.obj.pattern_properties {
|
||||
for (_, v) in pattern_props {
|
||||
for v in pattern_props.values_mut() {
|
||||
let mut inner = (**v).clone();
|
||||
f(&mut inner);
|
||||
*v = Arc::new(inner);
|
||||
|
||||
@ -1,3 +1,5 @@
|
||||
use std::collections::HashSet;
|
||||
|
||||
use crate::database::schema::Schema;
|
||||
use serde::{Deserialize, Serialize};
|
||||
use serde_json::Value;
|
||||
@ -19,6 +21,8 @@ pub struct Type {
|
||||
pub longevity: Option<i32>,
|
||||
#[serde(default)]
|
||||
pub hierarchy: Vec<String>,
|
||||
#[serde(default)]
|
||||
pub variations: HashSet<String>,
|
||||
pub relationship: Option<bool>,
|
||||
#[serde(default)]
|
||||
pub fields: Vec<String>,
|
||||
|
||||
Reference in New Issue
Block a user