Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 8aa15873b0 | |||
| 0d282cc930 | |||
| 581fc8e0c0 | |||
| 6f0bff8dc7 | |||
| 99c69e27ab |
@ -87,6 +87,34 @@
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"schedule": {
|
||||
"type": [
|
||||
"opening_hours",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
},
|
||||
"season": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": "string"
|
||||
}
|
||||
}
|
||||
},
|
||||
"opening_hours": {
|
||||
"type": "object",
|
||||
"properties": {
|
||||
"open": {
|
||||
"type": "string"
|
||||
},
|
||||
"seasons": {
|
||||
"type": "array",
|
||||
"items": {
|
||||
"type": "season"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -262,6 +290,7 @@
|
||||
"uuid_field",
|
||||
"tags",
|
||||
"ad_hoc",
|
||||
"schedule",
|
||||
"$and",
|
||||
"$or"
|
||||
],
|
||||
@ -277,6 +306,7 @@
|
||||
"uuid_field",
|
||||
"tags",
|
||||
"ad_hoc",
|
||||
"schedule",
|
||||
"$and",
|
||||
"$or"
|
||||
],
|
||||
@ -298,6 +328,7 @@
|
||||
"uuid_field",
|
||||
"tags",
|
||||
"ad_hoc",
|
||||
"schedule",
|
||||
"$and",
|
||||
"$or"
|
||||
],
|
||||
@ -366,6 +397,41 @@
|
||||
"string.condition",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"schedule": {
|
||||
"type": [
|
||||
"filter",
|
||||
"null"
|
||||
],
|
||||
"compiledPropertyNames": [
|
||||
"open",
|
||||
"seasons"
|
||||
],
|
||||
"properties": {
|
||||
"open": {
|
||||
"type": [
|
||||
"string.condition",
|
||||
"null"
|
||||
]
|
||||
},
|
||||
"seasons": {
|
||||
"type": [
|
||||
"filter",
|
||||
"null"
|
||||
],
|
||||
"compiledPropertyNames": [
|
||||
"label"
|
||||
],
|
||||
"properties": {
|
||||
"label": {
|
||||
"type": [
|
||||
"string.condition",
|
||||
"null"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"type": "filter"
|
||||
@ -483,10 +549,12 @@
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"opening_hours": {},
|
||||
"season": {}
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
]
|
||||
|
||||
@ -26,10 +26,14 @@ impl Schema {
|
||||
if let Some(items) = &child.obj.items {
|
||||
if !items.is_proxy() {
|
||||
structural_filter = items.compile_filter(_db, "", _errors);
|
||||
} else if let Some(target) = Self::value_type_target(items, _db) {
|
||||
structural_filter = target.compile_filter(_db, "", _errors);
|
||||
}
|
||||
}
|
||||
} else if !child.is_proxy() {
|
||||
structural_filter = child.compile_filter(_db, "", _errors);
|
||||
} else if let Some(target) = Self::value_type_target(child, _db) {
|
||||
structural_filter = target.compile_filter(_db, "", _errors);
|
||||
}
|
||||
|
||||
if let Some(mut inline_schema) = structural_filter {
|
||||
@ -117,6 +121,37 @@ impl Schema {
|
||||
None
|
||||
}
|
||||
|
||||
/// Resolves a pure type-pointer schema to a named non-table value type's own schema —
|
||||
/// a reusable, schema-only object (e.g. an operating-hours config). Naming a value type
|
||||
/// is a reuse choice, not a semantics choice: it filters structurally, exactly like an
|
||||
/// inline object. Table-backed boundaries keep the lazy {type}.filter reference instead.
|
||||
fn value_type_target<'a>(schema: &Arc<Schema>, db: &'a Database) -> Option<&'a Arc<Schema>> {
|
||||
let t = match &schema.obj.type_ {
|
||||
Some(SchemaTypeOrArray::Single(t)) => Some(t.as_str()),
|
||||
Some(SchemaTypeOrArray::Multiple(types)) => {
|
||||
types.iter().find(|t| *t != "null").map(|s| s.as_str())
|
||||
}
|
||||
None => None,
|
||||
}?;
|
||||
if matches!(
|
||||
t,
|
||||
"string" | "integer" | "number" | "boolean" | "object" | "array" | "null"
|
||||
) {
|
||||
return None;
|
||||
}
|
||||
if t.ends_with(".condition") || t.ends_with(".filter") {
|
||||
return None;
|
||||
}
|
||||
if db.enums.contains_key(t) {
|
||||
return None;
|
||||
}
|
||||
let base = t.split('.').next_back().unwrap_or(t);
|
||||
if db.types.contains_key(base) {
|
||||
return None;
|
||||
}
|
||||
db.schemas.get(t)
|
||||
}
|
||||
|
||||
fn resolve_filter_type(schema: &Arc<Schema>, db: &Database) -> Option<Vec<String>> {
|
||||
if let Some(type_) = &schema.obj.type_ {
|
||||
match type_ {
|
||||
@ -151,6 +186,7 @@ impl Schema {
|
||||
"number" => Some(vec!["number.condition".to_string()]),
|
||||
"boolean" => Some(vec!["boolean.condition".to_string()]),
|
||||
"object" => None, // Inline structures are ignored in Composed References
|
||||
"dict" => None, // Dynamic dictionary maps are ignored in Composed References
|
||||
"array" => {
|
||||
if let Some(items) = &schema.obj.items {
|
||||
return Self::resolve_filter_type(items, db);
|
||||
@ -164,8 +200,16 @@ impl Schema {
|
||||
} else if db.enums.contains_key(custom) {
|
||||
Some(vec![format!("{}.condition", custom)])
|
||||
} else {
|
||||
// Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built
|
||||
Some(vec![format!("{}.filter", custom)])
|
||||
// A Relational cross-boundary gets a reference to the target's dynamically built
|
||||
// .filter — but only a table-backed boundary has one. A named non-table value type
|
||||
// compiles structurally upstream (value_type_target); reaching here means it had
|
||||
// no compilable structure — omit it rather than emit a dangling .filter reference.
|
||||
let base = custom.split('.').next_back().unwrap_or(custom);
|
||||
if db.types.contains_key(base) {
|
||||
Some(vec![format!("{}.filter", custom)])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -125,6 +125,7 @@ impl Schema {
|
||||
child.compile(db, root_id, format!("{}/{}", path, k), errors);
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(items) = &self.obj.items {
|
||||
items.compile(db, root_id, format!("{}/items", path), errors);
|
||||
}
|
||||
@ -138,6 +139,7 @@ impl Schema {
|
||||
if let Some(child) = &self.obj.not {
|
||||
child.compile(db, root_id, format!("{}/not", path), errors);
|
||||
}
|
||||
|
||||
if let Some(child) = &self.obj.contains {
|
||||
child.compile(db, root_id, format!("{}/contains", path), errors);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user