1use std::collections::BTreeMap;
2
3use sora_ir::model::{ConfigIr, FieldIr, TypeIr, UnionIr};
4
5use crate::model::{ConfigData, RowData, TableData, Value};
6
7pub fn filter_config_data_by_ir(ir: &ConfigIr, data: &ConfigData) -> ConfigData {
8 ConfigData {
9 tables: ir
10 .tables
11 .iter()
12 .filter_map(|table| {
13 let source = data.tables.iter().find(|item| item.name == table.name)?;
14 Some(TableData {
15 name: table.name.clone(),
16 rows: source
17 .rows
18 .iter()
19 .map(|row| filter_row(ir, &table.fields, row))
20 .collect(),
21 })
22 })
23 .collect(),
24 }
25}
26
27fn filter_row(ir: &ConfigIr, fields: &[FieldIr], row: &RowData) -> RowData {
28 RowData {
29 values: fields
30 .iter()
31 .filter_map(|field| {
32 let value = row.values.get(&field.name)?;
33 Some((
34 field.name.clone(),
35 filter_value(ir, &field.ty, value).unwrap_or_else(|| value.clone()),
36 ))
37 })
38 .collect(),
39 }
40}
41
42fn filter_value(ir: &ConfigIr, ty: &TypeIr, value: &Value) -> Option<Value> {
43 match ty {
44 TypeIr::Struct(name) => {
45 let struct_ir = ir.structs.iter().find(|item| item.name == *name)?;
46 let Value::Object(object) = value else {
47 return Some(value.clone());
48 };
49 Some(Value::Object(filter_object(ir, &struct_ir.fields, object)))
50 }
51 TypeIr::Union(name) => {
52 let union_ir = ir.unions.iter().find(|item| item.name == *name)?;
53 filter_union(ir, union_ir, value)
54 }
55 TypeIr::List(element) | TypeIr::Set(element) | TypeIr::Array { element, .. } => {
56 let Value::List(values) = value else {
57 return Some(value.clone());
58 };
59 Some(Value::List(
60 values
61 .iter()
62 .map(|value| filter_value(ir, element, value).unwrap_or_else(|| value.clone()))
63 .collect(),
64 ))
65 }
66 TypeIr::Map {
67 key,
68 value: element,
69 } => {
70 let Value::List(values) = value else {
71 return Some(value.clone());
72 };
73 Some(Value::List(
74 values
75 .iter()
76 .map(|entry| filter_map_entry(ir, key, element, entry))
77 .collect(),
78 ))
79 }
80 TypeIr::Optional(element) => {
81 if matches!(value, Value::Null) {
82 Some(Value::Null)
83 } else {
84 filter_value(ir, element, value)
85 }
86 }
87 TypeIr::Bool
88 | TypeIr::I8
89 | TypeIr::U8
90 | TypeIr::I16
91 | TypeIr::U16
92 | TypeIr::I32
93 | TypeIr::U32
94 | TypeIr::I64
95 | TypeIr::Duration
96 | TypeIr::F32
97 | TypeIr::F64
98 | TypeIr::String
99 | TypeIr::Text
100 | TypeIr::Enum(_)
101 | TypeIr::Ref { .. } => Some(value.clone()),
102 }
103}
104
105fn filter_map_entry(ir: &ConfigIr, key_ty: &TypeIr, value_ty: &TypeIr, entry: &Value) -> Value {
106 let Value::List(items) = entry else {
107 return entry.clone();
108 };
109 if items.len() != 2 {
110 return entry.clone();
111 }
112
113 Value::List(vec![
114 filter_value(ir, key_ty, &items[0]).unwrap_or_else(|| items[0].clone()),
115 filter_value(ir, value_ty, &items[1]).unwrap_or_else(|| items[1].clone()),
116 ])
117}
118
119fn filter_union(ir: &ConfigIr, union_ir: &UnionIr, value: &Value) -> Option<Value> {
120 let Value::Object(object) = value else {
121 return Some(value.clone());
122 };
123 let Some(Value::String(variant_name)) = object.get(&union_ir.tag) else {
124 return Some(value.clone());
125 };
126 let Some(variant) = union_ir
127 .variants
128 .iter()
129 .find(|item| item.name == *variant_name)
130 else {
131 return Some(value.clone());
132 };
133
134 let mut filtered = filter_object(ir, &variant.fields, object);
135 filtered.insert(union_ir.tag.clone(), Value::String(variant_name.clone()));
136 Some(Value::Object(filtered))
137}
138
139fn filter_object(
140 ir: &ConfigIr,
141 fields: &[FieldIr],
142 object: &BTreeMap<String, Value>,
143) -> BTreeMap<String, Value> {
144 fields
145 .iter()
146 .filter_map(|field| {
147 let value = object.get(&field.name)?;
148 Some((
149 field.name.clone(),
150 filter_value(ir, &field.ty, value).unwrap_or_else(|| value.clone()),
151 ))
152 })
153 .collect()
154}