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::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}