validator refactor progress
This commit is contained in:
@ -1,101 +1,29 @@
|
||||
pub mod compiler;
|
||||
pub mod context;
|
||||
pub mod error;
|
||||
pub mod formats;
|
||||
pub mod instance;
|
||||
pub mod registry;
|
||||
pub mod result;
|
||||
pub mod rules;
|
||||
pub mod schema;
|
||||
pub mod util;
|
||||
|
||||
pub use context::ValidationContext;
|
||||
pub use error::ValidationError;
|
||||
pub use instance::{MutableInstance, ReadOnlyInstance};
|
||||
pub use result::ValidationResult;
|
||||
|
||||
use crate::validator::registry::Registry;
|
||||
use crate::validator::schema::Schema;
|
||||
use serde_json::Value;
|
||||
use std::collections::HashSet;
|
||||
use std::sync::Arc;
|
||||
|
||||
pub enum ResolvedRef<'a> {
|
||||
Local(&'a Schema),
|
||||
Global(&'a Schema, &'a Schema),
|
||||
}
|
||||
|
||||
pub struct Validator {
|
||||
pub registry: Registry,
|
||||
pub families: std::collections::HashMap<String, Arc<Schema>>,
|
||||
pub schemas: std::sync::Arc<std::collections::HashMap<String, crate::database::schema::Schema>>,
|
||||
}
|
||||
|
||||
impl Validator {
|
||||
pub fn from_punc_definition(
|
||||
enums: Option<&Value>,
|
||||
types: Option<&Value>,
|
||||
puncs: Option<&Value>,
|
||||
pub fn new(
|
||||
schemas: std::sync::Arc<std::collections::HashMap<String, crate::database::schema::Schema>>,
|
||||
) -> Self {
|
||||
let mut registry = Registry::new();
|
||||
let mut families = std::collections::HashMap::new();
|
||||
|
||||
let mut family_map: std::collections::HashMap<String, std::collections::HashSet<String>> =
|
||||
std::collections::HashMap::new();
|
||||
|
||||
if let Some(Value::Array(arr)) = types {
|
||||
for item in arr {
|
||||
if let Some(name) = item.get("name").and_then(|v| v.as_str()) {
|
||||
if let Some(hierarchy) = item.get("hierarchy").and_then(|v| v.as_array()) {
|
||||
for ancestor in hierarchy {
|
||||
if let Some(anc_str) = ancestor.as_str() {
|
||||
family_map
|
||||
.entry(anc_str.to_string())
|
||||
.or_default()
|
||||
.insert(name.to_string());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (family_name, members) in family_map {
|
||||
let object_refs: Vec<Value> = members
|
||||
.iter()
|
||||
.map(|s| serde_json::json!({ "$ref": s }))
|
||||
.collect();
|
||||
let schema_json = serde_json::json!({
|
||||
"oneOf": object_refs
|
||||
});
|
||||
if let Ok(schema) = serde_json::from_value::<Schema>(schema_json) {
|
||||
let compiled = crate::validator::compiler::Compiler::compile(schema, None);
|
||||
families.insert(family_name, compiled);
|
||||
}
|
||||
}
|
||||
|
||||
let mut cache_items = |items_val: Option<&Value>| {
|
||||
if let Some(Value::Array(arr)) = items_val {
|
||||
for item in arr {
|
||||
if let Some(Value::Array(schemas)) = item.get("schemas") {
|
||||
for schema_val in schemas {
|
||||
if let Ok(schema) = serde_json::from_value::<Schema>(schema_val.clone()) {
|
||||
registry.add(schema);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
cache_items(enums);
|
||||
cache_items(types);
|
||||
cache_items(puncs);
|
||||
|
||||
Self { registry, families }
|
||||
Self { schemas }
|
||||
}
|
||||
|
||||
pub fn get_schema_ids(&self) -> Vec<String> {
|
||||
self.registry.schemas.keys().cloned().collect()
|
||||
self.schemas.keys().cloned().collect()
|
||||
}
|
||||
|
||||
pub fn check_type(t: &str, val: &Value) -> bool {
|
||||
@ -116,148 +44,14 @@ impl Validator {
|
||||
}
|
||||
}
|
||||
|
||||
pub fn resolve_ref<'a>(
|
||||
&'a self,
|
||||
root: &'a Schema,
|
||||
ref_string: &str,
|
||||
scope: &str,
|
||||
) -> Option<(ResolvedRef<'a>, String)> {
|
||||
if ref_string.starts_with('#') {
|
||||
if let Some(indexrs) = &root.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(ref_string) {
|
||||
return Some((ResolvedRef::Local(s.as_ref()), ref_string.to_string()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(base) = url::Url::parse(scope) {
|
||||
if let Ok(joined) = base.join(ref_string) {
|
||||
let joined_str = joined.to_string();
|
||||
if let Some(indexrs) = &root.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(&joined_str) {
|
||||
return Some((ResolvedRef::Local(s.as_ref() as &Schema), joined_str));
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(decoded) = percent_encoding::percent_decode_str(&joined_str).decode_utf8() {
|
||||
let decoded_str = decoded.to_string();
|
||||
if decoded_str != joined_str {
|
||||
if let Some(indexrs) = &root.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(&decoded_str) {
|
||||
return Some((ResolvedRef::Local(s.as_ref() as &Schema), decoded_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(s) = self.registry.schemas.get(&joined_str) {
|
||||
return Some((ResolvedRef::Global(s.as_ref(), s.as_ref()), joined_str));
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if ref_string.starts_with('#') {
|
||||
let joined_str = format!("{}{}", scope, ref_string);
|
||||
|
||||
if let Some(indexrs) = &root.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(&joined_str) {
|
||||
return Some((ResolvedRef::Local(s.as_ref() as &Schema), joined_str));
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(decoded) = percent_encoding::percent_decode_str(&joined_str).decode_utf8() {
|
||||
let decoded_str = decoded.to_string();
|
||||
if decoded_str != joined_str {
|
||||
if let Some(indexrs) = &root.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(&decoded_str) {
|
||||
return Some((ResolvedRef::Local(s.as_ref() as &Schema), decoded_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(s) = self.registry.schemas.get(&joined_str) {
|
||||
return Some((ResolvedRef::Global(s.as_ref(), s.as_ref()), joined_str));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Ok(parsed) = url::Url::parse(ref_string) {
|
||||
let absolute = parsed.to_string();
|
||||
if let Some(indexrs) = &root.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(&absolute) {
|
||||
return Some((ResolvedRef::Local(s.as_ref()), absolute));
|
||||
}
|
||||
}
|
||||
|
||||
let resource_base = if let Some((base, _)) = absolute.split_once('#') {
|
||||
base
|
||||
} else {
|
||||
&absolute
|
||||
};
|
||||
|
||||
if let Some(compiled) = self.registry.schemas.get(resource_base) {
|
||||
if let Some(indexrs) = &compiled.obj.compiled_registry {
|
||||
if let Some(s) = indexrs.schemas.get(&absolute) {
|
||||
return Some((ResolvedRef::Global(compiled.as_ref(), s.as_ref()), absolute));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(compiled) = self.registry.schemas.get(ref_string) {
|
||||
return Some((
|
||||
ResolvedRef::Global(compiled.as_ref(), compiled.as_ref()),
|
||||
ref_string.to_string(),
|
||||
));
|
||||
}
|
||||
|
||||
None
|
||||
}
|
||||
|
||||
pub fn validate(
|
||||
&self,
|
||||
schema_id: &str,
|
||||
instance: &Value,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
if let Some(schema) = self.registry.schemas.get(schema_id) {
|
||||
let ctx = ValidationContext::new(
|
||||
self,
|
||||
schema,
|
||||
schema,
|
||||
ReadOnlyInstance(instance),
|
||||
vec![],
|
||||
HashSet::new(),
|
||||
false,
|
||||
false,
|
||||
);
|
||||
ctx.validate()
|
||||
} else {
|
||||
Err(ValidationError {
|
||||
code: "SCHEMA_NOT_FOUND".to_string(),
|
||||
message: format!("Schema {} not found", schema_id),
|
||||
path: "".to_string(),
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
pub fn mask(
|
||||
&self,
|
||||
schema_id: &str,
|
||||
instance: &mut Value,
|
||||
) -> Result<ValidationResult, ValidationError> {
|
||||
if let Some(schema) = self.registry.schemas.get(schema_id) {
|
||||
let ctx = ValidationContext::new(
|
||||
self,
|
||||
schema,
|
||||
schema,
|
||||
MutableInstance::new(instance),
|
||||
vec![],
|
||||
HashSet::new(),
|
||||
false,
|
||||
false,
|
||||
);
|
||||
let res = ctx.validate()?;
|
||||
Ok(res)
|
||||
if let Some(schema) = self.schemas.get(schema_id) {
|
||||
let ctx = ValidationContext::new(&self.schemas, schema, schema, instance, false, false);
|
||||
ctx.validate_scoped()
|
||||
} else {
|
||||
Err(ValidationError {
|
||||
code: "SCHEMA_NOT_FOUND".to_string(),
|
||||
|
||||
Reference in New Issue
Block a user