jspg cleanup
This commit is contained in:
@ -165,20 +165,8 @@ impl Schema {
|
||||
} else if db.enums.contains_key(custom) {
|
||||
Some(vec![format!("{}.condition", custom)])
|
||||
} else {
|
||||
// A named type gets a reference to its dynamically built .filter — either a
|
||||
// Relational cross-boundary (table-backed) or a named value type, whose filter
|
||||
// is likewise synthesized (see Database::compile_filters). A named type with no
|
||||
// compilable structure gets no filter — omit it rather than dangle a reference.
|
||||
let base = custom.split('.').next_back().unwrap_or(custom);
|
||||
let has_value_filter = db
|
||||
.schemas
|
||||
.get(custom)
|
||||
.map_or(false, |s| Database::is_value_filter_candidate(custom, s));
|
||||
if db.types.contains_key(base) || has_value_filter {
|
||||
Some(vec![format!("{}.filter", custom)])
|
||||
} else {
|
||||
None
|
||||
}
|
||||
// Assume anything else is a Relational cross-boundary that already has its own .filter dynamically built
|
||||
Some(vec![format!("{}.filter", custom)])
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -116,17 +116,99 @@ fn parse_and_match_mocks(sql: &str, mocks: &[Value]) -> Option<Vec<Value>> {
|
||||
return None;
|
||||
}
|
||||
|
||||
// The merger's existence read emits one SELECT per probe (id / lookup key)
|
||||
// joined by UNION. Match each arm independently against its own tables and
|
||||
// WHERE clause; a mock matching any arm is returned once, mirroring UNION's
|
||||
// dedup semantics.
|
||||
let union_regex = Regex::new(r"(?i)\s+UNION\s+(?:ALL\s+)?").ok()?;
|
||||
let arms: Vec<&str> = union_regex.split(sql).collect();
|
||||
// 1. Extract table name
|
||||
let table_regex = Regex::new(r#"(?i)\s+FROM\s+(?:[a-zA-Z_]\w*\.)?"?([a-zA-Z_]\w*)"?"#).ok()?;
|
||||
let table = if let Some(caps) = table_regex.captures(sql) {
|
||||
caps.get(1)?.as_str()
|
||||
} else {
|
||||
return None;
|
||||
};
|
||||
|
||||
// 2. Extract WHERE conditions string
|
||||
let mut where_clause = String::new();
|
||||
if let Some(where_idx) = sql_upper.find(" WHERE ") {
|
||||
let mut where_end = sql_upper.find(" ORDER BY ").unwrap_or(sql_upper.len());
|
||||
if let Some(limit_idx) = sql_upper.find(" LIMIT ") {
|
||||
if limit_idx < where_end {
|
||||
where_end = limit_idx;
|
||||
}
|
||||
}
|
||||
where_clause = sql[where_idx + 7..where_end].to_string();
|
||||
}
|
||||
|
||||
// 3. Find matching mocks
|
||||
let mut matches = Vec::new();
|
||||
let or_regex = Regex::new(r"(?i)\s+OR\s+").ok()?;
|
||||
let and_regex = Regex::new(r"(?i)\s+AND\s+").ok()?;
|
||||
|
||||
for mock in mocks {
|
||||
if let Some(mock_obj) = mock.as_object() {
|
||||
if arms.iter().any(|arm| arm_matches_mock(arm, mock_obj)) {
|
||||
if let Some(t) = mock_obj.get("type") {
|
||||
if t.as_str() != Some(table) {
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
if where_clause.is_empty() {
|
||||
matches.push(mock.clone());
|
||||
continue;
|
||||
}
|
||||
|
||||
let or_parts = or_regex.split(&where_clause);
|
||||
let mut any_branch_matched = false;
|
||||
|
||||
for or_part in or_parts {
|
||||
let branch_str = or_part.replace('(', "").replace(')', "");
|
||||
let mut branch_matches = true;
|
||||
|
||||
for part in and_regex.split(&branch_str) {
|
||||
if let Some(eq_idx) = part.find('=') {
|
||||
let left = part[..eq_idx]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
let right = part[eq_idx + 1..].trim().trim_matches('\'');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
Some(Value::Number(n)) => n.to_string(),
|
||||
Some(Value::Bool(b)) => b.to_string(),
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
if mock_val_str != right {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
} else if part.to_uppercase().contains(" IS NULL") {
|
||||
let left = part[..part.to_uppercase().find(" IS NULL").unwrap()]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
if mock_val_str != "null" {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if branch_matches {
|
||||
any_branch_matched = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if any_branch_matched {
|
||||
matches.push(mock.clone());
|
||||
}
|
||||
}
|
||||
@ -134,103 +216,3 @@ fn parse_and_match_mocks(sql: &str, mocks: &[Value]) -> Option<Vec<Value>> {
|
||||
|
||||
Some(matches)
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
fn arm_matches_mock(arm: &str, mock_obj: &serde_json::Map<String, Value>) -> bool {
|
||||
let arm_upper = arm.to_uppercase();
|
||||
|
||||
// 1. The mock's type must name one of the arm's tables. Hierarchy reads
|
||||
// SELECT FROM the root (entity) and JOIN the subtype tables, so every
|
||||
// FROM/JOIN table counts, not just the first.
|
||||
let table_regex = match Regex::new(r#"(?i)\b(?:FROM|JOIN)\s+(?:[a-zA-Z_]\w*\.)?"?([a-zA-Z_]\w*)"?"#) {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let tables: Vec<&str> = table_regex.captures_iter(arm).filter_map(|c| c.get(1)).map(|m| m.as_str()).collect();
|
||||
if tables.is_empty() {
|
||||
return false;
|
||||
}
|
||||
if let Some(t) = mock_obj.get("type") {
|
||||
if !tables.iter().any(|table| t.as_str() == Some(table)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
// 2. Extract this arm's WHERE conditions
|
||||
let mut where_clause = String::new();
|
||||
if let Some(where_idx) = arm_upper.find(" WHERE ") {
|
||||
let mut where_end = arm_upper.find(" ORDER BY ").unwrap_or(arm_upper.len());
|
||||
if let Some(limit_idx) = arm_upper.find(" LIMIT ") {
|
||||
if limit_idx < where_end {
|
||||
where_end = limit_idx;
|
||||
}
|
||||
}
|
||||
where_clause = arm[where_idx + 7..where_end].to_string();
|
||||
}
|
||||
|
||||
if where_clause.is_empty() {
|
||||
return true;
|
||||
}
|
||||
|
||||
// 3. Match the mock against the conditions
|
||||
let or_regex = match Regex::new(r"(?i)\s+OR\s+") {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
let and_regex = match Regex::new(r"(?i)\s+AND\s+") {
|
||||
Ok(r) => r,
|
||||
Err(_) => return false,
|
||||
};
|
||||
|
||||
for or_part in or_regex.split(&where_clause) {
|
||||
let branch_str = or_part.replace('(', "").replace(')', "");
|
||||
let mut branch_matches = true;
|
||||
|
||||
for part in and_regex.split(&branch_str) {
|
||||
if let Some(eq_idx) = part.find('=') {
|
||||
let left = part[..eq_idx]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
let right = part[eq_idx + 1..].trim().trim_matches('\'');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::String(s)) => s.clone(),
|
||||
Some(Value::Number(n)) => n.to_string(),
|
||||
Some(Value::Bool(b)) => b.to_string(),
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
if mock_val_str != right {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
} else if part.to_uppercase().contains(" IS NULL") {
|
||||
let left = part[..part.to_uppercase().find(" IS NULL").unwrap()]
|
||||
.trim()
|
||||
.split('.')
|
||||
.last()
|
||||
.unwrap_or("")
|
||||
.trim_matches('"');
|
||||
|
||||
let mock_val_str = match mock_obj.get(left) {
|
||||
Some(Value::Null) => "null".to_string(),
|
||||
_ => "".to_string(),
|
||||
};
|
||||
|
||||
if mock_val_str != "null" {
|
||||
branch_matches = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if branch_matches {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
false
|
||||
}
|
||||
|
||||
@ -262,23 +262,15 @@ impl Database {
|
||||
}
|
||||
}
|
||||
|
||||
/// Synthesizes Composed Filter References for all table-backed boundaries — and for
|
||||
/// named non-table value types (schema-only objects, e.g. an operating-hours config),
|
||||
/// so a property reference resolves to ONE named filter instead of inlining anonymous
|
||||
/// per-path copies (which duplicate identical leaf type names for downstream codegen).
|
||||
/// Synthesizes Composed Filter References for all table-backed boundaries.
|
||||
fn compile_filters(&mut self, errors: &mut Vec<crate::drop::Error>) -> Vec<(String, String)> {
|
||||
let mut filter_schemas = Vec::new();
|
||||
let mut seen_value_ids = std::collections::HashSet::new();
|
||||
for (type_name, type_def) in &self.types {
|
||||
for (id, schema_arc) in &type_def.schemas {
|
||||
// Run synthesis on structured table-backed boundaries and named value types.
|
||||
// Exclude subschemas!
|
||||
// Only run synthesis on actual structured, table-backed boundaries. Exclude subschemas!
|
||||
let base_name = id.split('.').last().unwrap_or(id);
|
||||
let is_table_backed = base_name == type_def.name;
|
||||
let is_value_type = !is_table_backed
|
||||
&& Self::is_value_filter_candidate(id, schema_arc)
|
||||
&& seen_value_ids.insert(id.clone());
|
||||
if (is_table_backed || is_value_type) && !id.contains('/') {
|
||||
if is_table_backed && !id.contains('/') {
|
||||
if let Some(filter_schema) = schema_arc.compile_filter(self, id, errors) {
|
||||
filter_schemas.push((
|
||||
type_name.clone(),
|
||||
@ -301,20 +293,6 @@ impl Database {
|
||||
filter_ids
|
||||
}
|
||||
|
||||
/// A named non-table value type that earns its own synthesized filter: a bare-named
|
||||
/// (dotless) schema-only object with compiled properties. The base `filter`/`condition`
|
||||
/// schemas are infrastructure, not value types.
|
||||
pub fn is_value_filter_candidate(id: &str, schema: &Arc<Schema>) -> bool {
|
||||
!id.contains('.')
|
||||
&& id != "filter"
|
||||
&& id != "condition"
|
||||
&& schema
|
||||
.obj
|
||||
.compiled_properties
|
||||
.get()
|
||||
.map_or(false, |props| !props.is_empty())
|
||||
}
|
||||
|
||||
/// Synthesizes strong Enum Conditions mirroring the string.condition capabilities.
|
||||
fn compile_conditions(&mut self) -> Vec<(String, String)> {
|
||||
let mut enum_conditions = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user