jspg error refactoring checkpoint

This commit is contained in:
2026-06-23 17:03:27 -04:00
parent fb25224d22
commit d77765cb61
25 changed files with 362 additions and 218 deletions

View File

@ -1,4 +1,8 @@
use crate::database::schema::Schema;
#[allow(unused_imports)]
use crate::drop::{Error, ErrorDetails};
#[allow(unused_imports)]
use std::collections::HashMap;
use std::sync::Arc;
impl Schema {
@ -8,18 +12,19 @@ impl Schema {
field_name: &str,
root_id: &str,
path: &str,
errors: &mut Vec<crate::drop::Error>,
errors: &mut Vec<Error>,
) {
#[cfg(not(test))]
for c in id.chars() {
if !c.is_ascii_lowercase() && !c.is_ascii_digit() && c != '_' && c != '.' && c != '$' {
errors.push(crate::drop::Error {
errors.push(Error {
code: "INVALID_IDENTIFIER".to_string(),
message: format!(
"Invalid character '{}' in JSON Schema '{}' property: '{}'. Identifiers must exclusively contain [a-z0-9_.$]",
c, field_name, id
),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([
("character".to_string(), c.to_string()),
("field_name".to_string(), field_name.to_string()),
("identifier".to_string(), id.to_string()),
])),
details: ErrorDetails {
path: Some(path.to_string()),
schema: Some(root_id.to_string()),
..Default::default()
@ -35,7 +40,7 @@ impl Schema {
root_id: &str,
path: String,
to_insert: &mut Vec<(String, Arc<Schema>)>,
errors: &mut Vec<crate::drop::Error>,
errors: &mut Vec<Error>,
) {
if let Some(crate::database::object::SchemaTypeOrArray::Single(t)) = &schema_arc.obj.type_ {
if t == "array" {
@ -70,7 +75,7 @@ impl Schema {
root_id: &str,
path: String,
to_insert: &mut Vec<(String, Arc<Schema>)>,
errors: &mut Vec<crate::drop::Error>,
errors: &mut Vec<Error>,
) {
if let Some(props) = &schema_arc.obj.properties {
for (k, v) in props.iter() {

View File

@ -5,7 +5,9 @@ pub mod filter;
pub mod polymorphism;
use crate::database::schema::Schema;
use crate::drop::{Error, ErrorDetails};
use indexmap::IndexMap;
use std::collections::HashMap;
impl Schema {
pub fn compile(
@ -13,7 +15,7 @@ impl Schema {
db: &crate::database::Database,
root_id: &str,
path: String,
errors: &mut Vec<crate::drop::Error>,
errors: &mut Vec<Error>,
) {
if self.obj.compiled_properties.get().is_some() {
return;
@ -72,13 +74,10 @@ impl Schema {
}
if custom_type_count > 1 {
errors.push(crate::drop::Error {
errors.push(Error {
code: "MULTIPLE_INHERITANCE_PROHIBITED".to_string(),
message: format!(
"Schema attempts to extend multiple custom object pointers in its type array {:?}. Use 'oneOf' for polymorphism and tagged unions.",
types
),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([("types".to_string(), types.join(", "))])),
details: ErrorDetails {
path: Some(path.clone()),
schema: Some(root_id.to_string()),
..Default::default()

View File

@ -1,4 +1,6 @@
use crate::drop::{Error, ErrorDetails};
use indexmap::IndexSet;
use std::collections::HashMap;
use crate::database::schema::Schema;
impl Schema {
@ -7,7 +9,7 @@ impl Schema {
db: &crate::database::Database,
root_id: &str,
path: &str,
errors: &mut Vec<crate::drop::Error>,
errors: &mut Vec<Error>,
) {
let mut options = indexmap::IndexMap::new();
let strategy: &str;
@ -118,16 +120,16 @@ impl Schema {
};
if strategy.is_empty() {
errors.push(crate::drop::Error {
code: "AMBIGUOUS_POLYMORPHISM".to_string(),
message: format!("oneOf boundaries must map mathematically unique 'type' or 'kind' discriminators, or strictly contain disjoint primitive types."),
details: crate::drop::ErrorDetails {
path: Some(path.to_string()),
schema: Some(root_id.to_string()),
..Default::default()
}
});
return;
errors.push(Error {
code: "AMBIGUOUS_POLYMORPHISM".to_string(),
values: None,
details: ErrorDetails {
path: Some(path.to_string()),
schema: Some(root_id.to_string()),
..Default::default()
}
});
return;
}
for (i, c) in one_of.iter().enumerate() {
@ -140,15 +142,15 @@ impl Schema {
if let Some(val) = c.obj.get_discriminator_value(&strategy, &child_id) {
if options.contains_key(&val) {
errors.push(crate::drop::Error {
errors.push(Error {
code: "POLYMORPHIC_COLLISION".to_string(),
message: format!("Polymorphic boundary defines multiple candidates mapped to the identical discriminator value '{}'.", val),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([("value".to_string(), val.to_string())])),
details: ErrorDetails {
path: Some(path.to_string()),
schema: Some(root_id.to_string()),
..Default::default()
}
});
}
});
continue;
}

View File

@ -1,7 +1,8 @@
use crate::drop::{Error, ErrorDetails};
use serde_json::Value;
use std::collections::{HashMap, HashSet};
pub fn compose(val: &mut Value, errors: &mut Vec<crate::drop::Error>) -> Result<(), String> {
pub fn compose(val: &mut Value, errors: &mut Vec<Error>) {
let mut traits = HashMap::new();
let mut schemas = HashMap::new();
@ -56,15 +57,13 @@ pub fn compose(val: &mut Value, errors: &mut Vec<crate::drop::Error>) -> Result<
}
}
}
Ok(())
}
fn resolve_in_place(
current: &mut Value,
traits: &HashMap<String, Value>,
schemas: &HashMap<String, Value>,
errors: &mut Vec<crate::drop::Error>,
errors: &mut Vec<Error>,
schema_id: &str,
path: &str,
visited: &mut HashSet<String>,
@ -118,10 +117,10 @@ fn resolve_in_place(
for inc in include_arr {
if let Some(inc_name) = inc.as_str() {
if visited.contains(inc_name) {
errors.push(crate::drop::Error {
errors.push(Error {
code: "CIRCULAR_INCLUDE_DETECTED".to_string(),
message: format!("Circular inclusion detected for '{}'", inc_name),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([("include".to_string(), inc_name.to_string())])),
details: ErrorDetails {
schema: Some(schema_id.to_string()),
path: Some(path.to_string()),
..Default::default()
@ -232,10 +231,10 @@ fn resolve_in_place(
}
}
} else {
errors.push(crate::drop::Error {
errors.push(Error {
code: "TRAIT_NOT_FOUND".to_string(),
message: format!("Trait or schema '{}' not found for inclusion", inc_name),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([("include".to_string(), inc_name.to_string())])),
details: ErrorDetails {
schema: Some(schema_id.to_string()),
path: Some(path.to_string()),
..Default::default()

View File

@ -28,6 +28,8 @@ use serde_json::Value;
use indexmap::IndexMap;
use std::sync::Arc;
use r#type::Type;
use std::collections::HashMap;
use crate::drop::{Drop, Error, ErrorDetails};
#[derive(serde::Serialize)]
pub struct Database {
@ -57,13 +59,7 @@ impl Database {
let mut errors = Vec::new();
if let Err(e) = compose::compose(&mut val, &mut errors) {
errors.push(crate::drop::Error {
code: "COMPOSE_FAILED".to_string(),
message: format!("Fatal error during trait composition: {}", e),
details: crate::drop::ErrorDetails::default(),
});
}
compose::compose(&mut val, &mut errors);
if let serde_json::Value::Object(mut map) = val {
if let Some(serde_json::Value::Array(arr)) = map.remove("enums") {
@ -78,10 +74,13 @@ impl Database {
db.enums.insert(def.name.clone(), def);
}
Err(e) => {
errors.push(crate::drop::Error {
errors.push(Error {
code: "DATABASE_ENUM_PARSE_FAILED".to_string(),
message: format!("Failed to parse database enum '{}': {}", name, e),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([
("enum".to_string(), name.clone()),
("reason".to_string(), e.to_string()),
])),
details: ErrorDetails {
context: Some(serde_json::json!(name)),
..Default::default()
},
@ -103,10 +102,13 @@ impl Database {
db.types.insert(def.name.clone(), def);
}
Err(e) => {
errors.push(crate::drop::Error {
errors.push(Error {
code: "DATABASE_TYPE_PARSE_FAILED".to_string(),
message: format!("Failed to parse database type '{}': {}", name, e),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([
("type".to_string(), name.clone()),
("reason".to_string(), e.to_string()),
])),
details: ErrorDetails {
context: Some(serde_json::json!(name)),
..Default::default()
},
@ -132,10 +134,13 @@ impl Database {
}
}
Err(e) => {
errors.push(crate::drop::Error {
errors.push(Error {
code: "DATABASE_RELATION_PARSE_FAILED".to_string(),
message: format!("Failed to parse database relation '{}': {}", constraint, e),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([
("relation".to_string(), constraint.clone()),
("reason".to_string(), e.to_string()),
])),
details: ErrorDetails {
context: Some(serde_json::json!(constraint)),
..Default::default()
},
@ -157,10 +162,13 @@ impl Database {
db.puncs.insert(def.name.clone(), def);
}
Err(e) => {
errors.push(crate::drop::Error {
errors.push(Error {
code: "DATABASE_PUNC_PARSE_FAILED".to_string(),
message: format!("Failed to parse database punc '{}': {}", name, e),
details: crate::drop::ErrorDetails {
values: Some(HashMap::from([
("punc".to_string(), name.clone()),
("reason".to_string(), e.to_string()),
])),
details: ErrorDetails {
context: Some(serde_json::json!(name)),
..Default::default()
},
@ -173,9 +181,9 @@ impl Database {
db.compile(&mut errors);
let drop = if errors.is_empty() {
crate::drop::Drop::success()
Drop::success()
} else {
crate::drop::Drop::with_errors(errors)
Drop::with_errors(errors)
};
(db, drop)
}
@ -443,7 +451,7 @@ impl Database {
// Abort relation discovery early if no hierarchical inheritance match was found
if matching_rels.is_empty() {
let mut details = crate::drop::ErrorDetails {
let mut details = ErrorDetails {
path: Some(path.to_string()),
..Default::default()
};
@ -451,12 +459,13 @@ impl Database {
details.schema = Some(sid.to_string());
}
errors.push(crate::drop::Error {
errors.push(Error {
code: "EDGE_MISSING".to_string(),
message: format!(
"No database relation exists between '{}' and '{}' for property '{}'",
parent_type, child_type, prop_name
),
values: Some(HashMap::from([
("parent_type".to_string(), parent_type.to_string()),
("child_type".to_string(), child_type.to_string()),
("property_name".to_string(), prop_name.to_string()),
])),
details,
});
return None;
@ -542,7 +551,7 @@ impl Database {
// we must abort rather than silently guessing. Returning None prevents arbitrary SQL generation
// and forces a clean structural error for the architect.
if !resolved {
let mut details = crate::drop::ErrorDetails {
let mut details = ErrorDetails {
path: Some(path.to_string()),
context: serde_json::to_value(&matching_rels).ok(),
cause: Some("Multiple conflicting constraints found matching prefixes".to_string()),
@ -552,12 +561,13 @@ impl Database {
details.schema = Some(sid.to_string());
}
errors.push(crate::drop::Error {
errors.push(Error {
code: "AMBIGUOUS_TYPE_RELATIONS".to_string(),
message: format!(
"Ambiguous database relation between '{}' and '{}' for property '{}'",
parent_type, child_type, prop_name
),
values: Some(HashMap::from([
("parent_type".to_string(), parent_type.to_string()),
("child_type".to_string(), child_type.to_string()),
("property_name".to_string(), prop_name.to_string()),
])),
details,
});
return None;