Skip to main content

sora_data/
scope.rs

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::I32
89        | TypeIr::I64
90        | TypeIr::F32
91        | TypeIr::F64
92        | TypeIr::String
93        | TypeIr::Enum(_)
94        | TypeIr::Ref { .. } => Some(value.clone()),
95    }
96}
97
98fn filter_map_entry(ir: &ConfigIr, key_ty: &TypeIr, value_ty: &TypeIr, entry: &Value) -> Value {
99    let Value::List(items) = entry else {
100        return entry.clone();
101    };
102    if items.len() != 2 {
103        return entry.clone();
104    }
105
106    Value::List(vec![
107        filter_value(ir, key_ty, &items[0]).unwrap_or_else(|| items[0].clone()),
108        filter_value(ir, value_ty, &items[1]).unwrap_or_else(|| items[1].clone()),
109    ])
110}
111
112fn filter_union(ir: &ConfigIr, union_ir: &UnionIr, value: &Value) -> Option<Value> {
113    let Value::Object(object) = value else {
114        return Some(value.clone());
115    };
116    let Some(Value::String(variant_name)) = object.get(&union_ir.tag) else {
117        return Some(value.clone());
118    };
119    let Some(variant) = union_ir
120        .variants
121        .iter()
122        .find(|item| item.name == *variant_name)
123    else {
124        return Some(value.clone());
125    };
126
127    let mut filtered = filter_object(ir, &variant.fields, object);
128    filtered.insert(union_ir.tag.clone(), Value::String(variant_name.clone()));
129    Some(Value::Object(filtered))
130}
131
132fn filter_object(
133    ir: &ConfigIr,
134    fields: &[FieldIr],
135    object: &BTreeMap<String, Value>,
136) -> BTreeMap<String, Value> {
137    fields
138        .iter()
139        .filter_map(|field| {
140            let value = object.get(&field.name)?;
141            Some((
142                field.name.clone(),
143                filter_value(ir, &field.ty, value).unwrap_or_else(|| value.clone()),
144            ))
145        })
146        .collect()
147}