Skip to main content

siglus_scene_vm/runtime/forms/
database.rs

1use anyhow::Result;
2
3use crate::runtime::forms::prop_access;
4use crate::runtime::{CommandContext, Value};
5
6fn composite_db_op_key(db_no: i32, op: i32) -> i32 {
7    ((db_no & 0x7fff) << 16) ^ (op & 0xffff)
8}
9
10fn ref_chain_from_value(v: &Value) -> Option<(Option<i32>, &[i32])> {
11    match v {
12        Value::Element(chain) => Some((None, chain.as_slice())),
13        Value::NamedArg { id, value } => match value.as_ref() {
14            Value::Element(chain) => Some((Some(*id), chain.as_slice())),
15            _ => None,
16        },
17        _ => None,
18    }
19}
20
21fn database_get_data(
22    ctx: &mut CommandContext,
23    db: &siglus_assets::dbs::DbsDatabase,
24    item_call_no: i32,
25    refs: &[Value],
26) {
27    let mut positional_column_no = 0i32;
28    for arg in refs {
29        let Some((explicit_column_no, chain)) = ref_chain_from_value(arg) else {
30            positional_column_no += 1;
31            continue;
32        };
33        let column_call_no = explicit_column_no.unwrap_or(positional_column_no);
34        positional_column_no += 1;
35
36        match db.check_column_no(column_call_no) {
37            1 => {
38                if let Ok(Some(v)) = db.get_data_int(item_call_no, column_call_no) {
39                    prop_access::assign_to_chain(ctx, chain, Value::Int(v as i64));
40                } else {
41                    prop_access::assign_to_chain(ctx, chain, Value::Int(0));
42                }
43            }
44            2 => {
45                if let Ok(Some(v)) = db.get_data_str(item_call_no, column_call_no) {
46                    prop_access::assign_to_chain(ctx, chain, Value::Str(v));
47                } else {
48                    prop_access::assign_to_chain(ctx, chain, Value::Str(String::new()));
49                }
50            }
51            _ => {
52                prop_access::assign_to_chain(ctx, chain, Value::Int(0));
53            }
54        }
55    }
56}
57
58pub fn dispatch(ctx: &mut CommandContext, form_id: u32, args: &[Value]) -> Result<bool> {
59    let parsed = prop_access::parse_element_chain_ctx(ctx, form_id, args);
60    let (chain_pos, chain) = match parsed {
61        Some((pos, ch)) if ch.len() >= 2 => (Some(pos), Some(ch)),
62        _ => (None, None),
63    };
64
65    if let Some(chain) = chain {
66        let op = chain[1];
67        let params = if let Some(pos) = chain_pos {
68            prop_access::script_args(args, pos)
69        } else {
70            &[]
71        };
72        let p_i32 =
73            |i: usize| -> i32 { params.get(i).and_then(|v| v.as_i64()).unwrap_or(0) as i32 };
74        let p_str = |i: usize| -> &str { params.get(i).and_then(|v| v.as_str()).unwrap_or("") };
75
76        if ctx.ids.database_list_get_size != 0 && op == ctx.ids.database_list_get_size {
77            if ctx.globals.database_off {
78                ctx.push(Value::Int(0));
79            } else {
80                ctx.push(Value::Int(ctx.tables.databases.len() as i64));
81            }
82            return Ok(true);
83        }
84
85        if op == ctx.ids.elm_array {
86            if chain.len() < 4 {
87                ctx.push(Value::Int(0));
88                return Ok(true);
89            }
90
91            let db_no = chain[2];
92            if db_no < 0 {
93                ctx.push(Value::Int(0));
94                return Ok(true);
95            }
96
97            if ctx.globals.database_off {
98                let db_op = chain[3];
99                if ctx.ids.database_get_str != 0 && db_op == ctx.ids.database_get_str {
100                    ctx.push(Value::Str(String::new()));
101                } else {
102                    ctx.push(Value::Int(0));
103                }
104                return Ok(true);
105            }
106
107            let db = match ctx.tables.databases.get(db_no as usize).cloned() {
108                Some(db) => db,
109                None => {
110                    ctx.push(Value::Int(0));
111                    return Ok(true);
112                }
113            };
114
115            let db_op = chain[3];
116
117            if db_op == ctx.ids.database_get_num {
118                let item_call_no = p_i32(0);
119                let col_call_no = p_i32(1);
120                let mut out: i64 = 0;
121                if let Ok(Some(v)) = db.get_data_int(item_call_no, col_call_no) {
122                    out = v as i64;
123                }
124                ctx.push(Value::Int(out));
125                return Ok(true);
126            }
127
128            if ctx.ids.database_get_str != 0 && db_op == ctx.ids.database_get_str {
129                let item_call_no = p_i32(0);
130                let col_call_no = p_i32(1);
131                let mut out = String::new();
132                if let Ok(Some(v)) = db.get_data_str(item_call_no, col_call_no) {
133                    out = v;
134                }
135                ctx.push(Value::Str(out));
136                return Ok(true);
137            }
138
139            if ctx.ids.database_get_data != 0 && db_op == ctx.ids.database_get_data {
140                let item_call_no = p_i32(0);
141                let refs = if params.len() > 1 { &params[1..] } else { &[] };
142                database_get_data(ctx, &db, item_call_no, refs);
143                ctx.push(Value::Int(0));
144                return Ok(true);
145            }
146
147            if ctx.ids.database_check_item != 0 && db_op == ctx.ids.database_check_item {
148                let item_call_no = p_i32(0);
149                ctx.push(Value::Int(db.check_item_no(item_call_no) as i64));
150                return Ok(true);
151            }
152
153            if ctx.ids.database_check_column != 0 && db_op == ctx.ids.database_check_column {
154                let col_call_no = p_i32(0);
155                ctx.push(Value::Int(db.check_column_no(col_call_no) as i64));
156                return Ok(true);
157            }
158
159            if ctx.ids.database_find_num != 0 && db_op == ctx.ids.database_find_num {
160                let col_call_no = p_i32(0);
161                let value = p_i32(1);
162                let out = db.find_num(col_call_no, value).unwrap_or(-1);
163                ctx.push(Value::Int(out as i64));
164                return Ok(true);
165            }
166
167            if ctx.ids.database_find_str != 0 && db_op == ctx.ids.database_find_str {
168                let col_call_no = p_i32(0);
169                let needle = p_str(1);
170                let out = db.find_str(col_call_no, needle).unwrap_or(-1);
171                ctx.push(Value::Int(out as i64));
172                return Ok(true);
173            }
174
175            if ctx.ids.database_find_str_real != 0 && db_op == ctx.ids.database_find_str_real {
176                let col_call_no = p_i32(0);
177                let needle = p_str(1);
178                let out = db.find_str_real(col_call_no, needle).unwrap_or(-1);
179                ctx.push(Value::Int(out as i64));
180                return Ok(true);
181            }
182
183            let prop_key = composite_db_op_key(db_no, db_op);
184            prop_access::store_or_push_prop(ctx, form_id, prop_key, chain_pos.unwrap(), args);
185            return Ok(true);
186        }
187
188        prop_access::store_or_push_prop(ctx, form_id, op, chain_pos.unwrap(), args);
189        return Ok(true);
190    }
191
192    Ok(false)
193}