jspg progress

This commit is contained in:
2026-02-17 21:46:10 -05:00
parent 32ed463df8
commit 623c34c0bc
20 changed files with 3566 additions and 1094 deletions

View File

@ -1,7 +1,7 @@
use crate::schema::Schema;
use regex::Regex;
use serde_json::Value;
use std::collections::HashMap;
// use std::collections::HashMap;
use std::error::Error;
use std::sync::Arc;
@ -14,14 +14,6 @@ pub enum CompiledFormat {
Regex(Regex),
}
/// A fully compiled schema with a root node and a pre-calculated index map.
/// This allows O(1) lookup of any anchor or $id within the schema tree.
#[derive(Debug, Clone)]
pub struct CompiledSchema {
pub root: Arc<Schema>,
pub index: HashMap<String, Arc<Schema>>,
}
/// A wrapper for compiled regex patterns
#[derive(Debug, Clone)]
pub struct CompiledRegex(pub Regex);
@ -169,10 +161,10 @@ impl Compiler {
}
}
/// Recursively traverses the schema tree to build a map of all internal Anchors ($id) and JSON Pointers.
/// Recursively traverses the schema tree to build the local registry index.
fn compile_index(
schema: &Arc<Schema>,
index: &mut HashMap<String, Arc<Schema>>,
registry: &mut crate::registry::Registry,
parent_base: Option<String>,
pointer: json_pointer::JsonPointer<String, Vec<String>>,
) {
@ -186,15 +178,14 @@ impl Compiler {
} else {
format!("{}#{}", base, fragment)
};
index.insert(ptr_uri, schema.clone());
registry.insert(ptr_uri, schema.clone());
}
// 2. Determine Current Scope... (unchanged logic, just use pointer)
// 2. Determine Current Scope... (unchanged logic)
let mut current_base = parent_base.clone();
let mut child_pointer = pointer.clone();
if let Some(id) = &schema.obj.id {
// ... resolve ID logic ...
let mut new_base = None;
if let Ok(_) = url::Url::parse(id) {
new_base = Some(id.clone());
@ -209,40 +200,29 @@ impl Compiler {
}
if let Some(base) = new_base {
index.insert(base.clone(), schema.clone());
// println!("DEBUG: Compiling index for path: {}", base); // Added println
registry.insert(base.clone(), schema.clone());
current_base = Some(base);
child_pointer = json_pointer::JsonPointer::new(vec![]); // Reset
}
}
// 3. Index by Anchor (unchanged)
if let Some(anchor) = &schema.obj.anchor {
if let Some(base) = &current_base {
let anchor_uri = format!("{}#{}", base, anchor);
index.insert(anchor_uri, schema.clone());
}
}
// Index by Dynamic Anchor
if let Some(d_anchor) = &schema.obj.dynamic_anchor {
if let Some(base) = &current_base {
let anchor_uri = format!("{}#{}", base, d_anchor);
index.insert(anchor_uri.clone(), schema.clone());
println!("Indexed Dynamic Anchor: {}", anchor_uri);
}
}
// 3. Index by Anchor
if let Some(anchor) = &schema.obj.anchor {
if let Some(base) = &current_base {
let anchor_uri = format!("{}#{}", base, anchor);
index.insert(anchor_uri.clone(), schema.clone());
println!("Indexed Anchor: {}", anchor_uri);
registry.insert(anchor_uri, schema.clone());
}
}
// Index by Dynamic Anchor
if let Some(d_anchor) = &schema.obj.dynamic_anchor {
if let Some(base) = &current_base {
let anchor_uri = format!("{}#{}", base, d_anchor);
registry.insert(anchor_uri, schema.clone());
}
}
// ... (Const/Enum indexing skipped for brevity, relies on string)
// 4. Recurse
// 4. Recurse (unchanged logic structure, just passing registry)
if let Some(defs) = schema.defs.as_ref().or(schema.definitions.as_ref()) {
let segment = if schema.defs.is_some() {
"$defs"
@ -252,10 +232,9 @@ impl Compiler {
for (key, sub_schema) in defs {
let mut sub = child_pointer.clone();
sub.push(segment.to_string());
// Decode key to avoid double encoding by JsonPointer
let decoded_key = percent_encoding::percent_decode_str(key).decode_utf8_lossy();
sub.push(decoded_key.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
@ -264,14 +243,14 @@ impl Compiler {
let mut sub = child_pointer.clone();
sub.push("properties".to_string());
sub.push(key.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(items) = &schema.items {
let mut sub = child_pointer.clone();
sub.push("items".to_string());
Self::compile_index(items, index, current_base.clone(), sub);
Self::compile_index(items, registry, current_base.clone(), sub);
}
if let Some(prefix_items) = &schema.prefix_items {
@ -279,7 +258,7 @@ impl Compiler {
let mut sub = child_pointer.clone();
sub.push("prefixItems".to_string());
sub.push(i.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
@ -288,7 +267,7 @@ impl Compiler {
let mut sub = child_pointer.clone();
sub.push("allOf".to_string());
sub.push(i.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(any_of) = &schema.any_of {
@ -296,7 +275,7 @@ impl Compiler {
let mut sub = child_pointer.clone();
sub.push("anyOf".to_string());
sub.push(i.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(one_of) = &schema.one_of {
@ -304,36 +283,36 @@ impl Compiler {
let mut sub = child_pointer.clone();
sub.push("oneOf".to_string());
sub.push(i.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(not) = &schema.not {
let mut sub = child_pointer.clone();
sub.push("not".to_string());
Self::compile_index(not, index, current_base.clone(), sub);
Self::compile_index(not, registry, current_base.clone(), sub);
}
if let Some(if_) = &schema.if_ {
let mut sub = child_pointer.clone();
sub.push("if".to_string());
Self::compile_index(if_, index, current_base.clone(), sub);
Self::compile_index(if_, registry, current_base.clone(), sub);
}
if let Some(then_) = &schema.then_ {
let mut sub = child_pointer.clone();
sub.push("then".to_string());
Self::compile_index(then_, index, current_base.clone(), sub);
Self::compile_index(then_, registry, current_base.clone(), sub);
}
if let Some(else_) = &schema.else_ {
let mut sub = child_pointer.clone();
sub.push("else".to_string());
Self::compile_index(else_, index, current_base.clone(), sub);
Self::compile_index(else_, registry, current_base.clone(), sub);
}
if let Some(deps) = &schema.dependent_schemas {
for (key, sub_schema) in deps {
let mut sub = child_pointer.clone();
sub.push("dependentSchemas".to_string());
sub.push(key.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(pp) = &schema.pattern_properties {
@ -341,55 +320,67 @@ impl Compiler {
let mut sub = child_pointer.clone();
sub.push("patternProperties".to_string());
sub.push(key.to_string());
Self::compile_index(sub_schema, index, current_base.clone(), sub);
Self::compile_index(sub_schema, registry, current_base.clone(), sub);
}
}
if let Some(contains) = &schema.contains {
let mut sub = child_pointer.clone();
sub.push("contains".to_string());
Self::compile_index(contains, index, current_base.clone(), sub);
Self::compile_index(contains, registry, current_base.clone(), sub);
}
if let Some(property_names) = &schema.property_names {
let mut sub = child_pointer.clone();
sub.push("propertyNames".to_string());
Self::compile_index(property_names, index, current_base.clone(), sub);
Self::compile_index(property_names, registry, current_base.clone(), sub);
}
}
/// Resolves a format string to a CompiledFormat (future optimization)
pub fn compile_format(_format: &str) -> Option<CompiledFormat> {
None
}
pub fn compile(mut root_schema: Schema, root_id: Option<String>) -> CompiledSchema {
// 1. Compile in-place (formats/regexes)
pub fn compile(mut root_schema: Schema, root_id: Option<String>) -> Arc<Schema> {
// 1. Compile in-place (formats/regexes/normalization)
Self::compile_formats_and_regexes(&mut root_schema);
// Apply root_id override if schema ID is missing
if let Some(ref rid) = root_id {
if let Some(rid) = &root_id {
if root_schema.obj.id.is_none() {
root_schema.obj.id = Some(rid.clone());
}
}
// 2. Wrap in Arc
let root = Arc::new(root_schema);
let mut index = HashMap::new();
// 2. Build ID/Pointer Index
let mut registry = crate::registry::Registry::new();
// We need a temporary Arc to satisfy compile_index recursion
// But we are modifying root_schema.
// This is tricky. compile_index takes &Arc<Schema>.
// We should build the index first, THEN attach it.
let root = Arc::new(root_schema);
// Default base_uri to ""
let base_uri = root_id
.clone()
.or_else(|| root.obj.id.clone())
.or(Some("".to_string()));
// 3. Build ID/Pointer Index
// Default base_uri to "" so that pointers like "#/foo" are indexed even if no root ID exists
Self::compile_index(
&root,
&mut index,
root_id.clone().or(Some("".to_string())),
&mut registry,
base_uri,
json_pointer::JsonPointer::new(vec![]),
);
// Also ensure root id is indexed if present
if let Some(rid) = root_id {
index.insert(rid, root.clone());
registry.insert(rid, root.clone());
}
CompiledSchema { root, index }
// Now we need to attach this registry to the root schema.
// Since root is an Arc, we might need to recreate it if we can't mutate.
// Schema struct modifications require &mut.
let mut final_schema = Arc::try_unwrap(root).unwrap_or_else(|arc| (*arc).clone());
final_schema.obj.compiled_schemas = Some(Arc::new(registry));
Arc::new(final_schema)
}
}