Skip to main content

kui_ffi/
value.rs

1//! `KuiValue`: the caller-owned payloads that ride on events and clicks.
2
3use super::*;
4
5// ---------------------------------------------------------------------------
6// Values
7
8/// A new null value, owned by the caller. Where a tag is taken it asks
9/// for the events without a tag.
10#[unsafe(no_mangle)]
11pub extern "C" fn kui_value_null() -> *mut KuiValue {
12    Box::into_raw(Box::new(KuiValue(Value::Null)))
13}
14
15/// A new boolean value, owned by the caller.
16#[unsafe(no_mangle)]
17pub extern "C" fn kui_value_bool(v: bool) -> *mut KuiValue {
18    Box::into_raw(Box::new(KuiValue(Value::Bool(v))))
19}
20
21/// A new integer value, owned by the caller.
22#[unsafe(no_mangle)]
23pub extern "C" fn kui_value_int(v: i64) -> *mut KuiValue {
24    Box::into_raw(Box::new(KuiValue(Value::Int(v))))
25}
26
27/// A new float value, owned by the caller.
28#[unsafe(no_mangle)]
29pub extern "C" fn kui_value_float(v: f64) -> *mut KuiValue {
30    Box::into_raw(Box::new(KuiValue(Value::Float(v))))
31}
32
33/// A new string value holding a copy of `s`, owned by the caller.
34#[unsafe(no_mangle)]
35pub extern "C" fn kui_value_str(s: KuiStr) -> *mut KuiValue {
36    guard(std::ptr::null_mut(), || {
37        Box::into_raw(Box::new(KuiValue(Value::Str(kstr(s).into_owned()))))
38    })
39}
40
41/// A new empty map, owned by the caller; fill it with
42/// [`kui_value_map_set`]. The usual shape of a tag is a map with a
43/// `kind` string.
44#[unsafe(no_mangle)]
45pub extern "C" fn kui_value_map() -> *mut KuiValue {
46    Box::into_raw(Box::new(KuiValue(Value::Map(Vec::new()))))
47}
48
49/// A new empty list, owned by the caller; fill it with
50/// [`kui_value_list_push`].
51#[unsafe(no_mangle)]
52pub extern "C" fn kui_value_list() -> *mut KuiValue {
53    Box::into_raw(Box::new(KuiValue(Value::List(Vec::new()))))
54}
55
56/// Appends to a list value. Consumes `val`; on anything but a list it is
57/// dropped and nothing changes.
58#[unsafe(no_mangle)]
59pub extern "C" fn kui_value_list_push(list: *mut KuiValue, val: *mut KuiValue) {
60    guard((), || {
61        if val.is_null() {
62            return;
63        }
64        let val = unsafe { Box::from_raw(val) };
65        let Some(list) = (unsafe { list.as_mut() }) else {
66            return;
67        };
68        if let Value::List(items) = &mut list.0 {
69            items.push(val.0);
70        }
71    });
72}
73
74/// Sets `key` on a map value, replacing an existing entry. Consumes `val`;
75/// on anything but a map it is dropped and nothing changes.
76#[unsafe(no_mangle)]
77pub extern "C" fn kui_value_map_set(map: *mut KuiValue, key: KuiStr, val: *mut KuiValue) {
78    guard((), || {
79        if val.is_null() {
80            return;
81        }
82        let val = unsafe { Box::from_raw(val) };
83        let Some(map) = (unsafe { map.as_mut() }) else {
84            return;
85        };
86        if let Value::Map(entries) = &mut map.0 {
87            let key = kstr(key).into_owned();
88            if let Some(e) = entries.iter_mut().find(|(k, _)| *k == key) {
89                e.1 = val.0;
90            } else {
91                entries.push((key, val.0));
92            }
93        }
94    });
95}
96
97/// Borrowed lookup on a map value; NULL if absent. Valid as long as the map.
98#[unsafe(no_mangle)]
99pub extern "C" fn kui_value_get(v: *const KuiValue, key: KuiStr) -> *const KuiValue {
100    guard(std::ptr::null(), || {
101        let Some(v) = (unsafe { v.as_ref() }) else {
102            return std::ptr::null();
103        };
104        match v.0.get(&kstr(key)) {
105            // Value and KuiValue are layout-identical (single field).
106            Some(inner) => (inner as *const Value).cast(),
107            None => std::ptr::null(),
108        }
109    })
110}
111
112/// How many entries a list or map holds; 0 for anything else, which a
113/// scalar reader tells apart from an empty one.
114#[unsafe(no_mangle)]
115pub extern "C" fn kui_value_len(v: *const KuiValue) -> usize {
116    guard(0, || match unsafe { v.as_ref() }.map(|v| &v.0) {
117        Some(Value::List(items)) => items.len(),
118        Some(Value::Map(entries)) => entries.len(),
119        _ => 0,
120    })
121}
122
123/// Borrowed entry `i` of a list value (`preedit`'s `cursor`, the two ends
124/// an `access` request carries); NULL past the end or on anything but a
125/// list. Valid as long as the list.
126#[unsafe(no_mangle)]
127pub extern "C" fn kui_value_at(v: *const KuiValue, i: usize) -> *const KuiValue {
128    guard(std::ptr::null(), || {
129        match unsafe { v.as_ref() }.map(|v| &v.0) {
130            // Value and KuiValue are layout-identical (single field).
131            Some(Value::List(items)) => items
132                .get(i)
133                .map_or(std::ptr::null(), |inner| (inner as *const Value).cast()),
134            _ => std::ptr::null(),
135        }
136    })
137}
138
139/// A map's `i`th entry, for walking one whose keys you do not know: the
140/// key into `*key` and the borrowed value returned, NULL past the end or
141/// on anything but a map. Entries keep the order they were set in.
142#[unsafe(no_mangle)]
143pub extern "C" fn kui_value_entry(
144    v: *const KuiValue,
145    i: usize,
146    key: *mut KuiStr,
147) -> *const KuiValue {
148    guard(std::ptr::null(), || {
149        match unsafe { v.as_ref() }.map(|v| &v.0) {
150            Some(Value::Map(entries)) => match entries.get(i) {
151                Some((k, inner)) => {
152                    if !key.is_null() {
153                        unsafe {
154                            key.write(KuiStr {
155                                ptr: k.as_ptr(),
156                                len: k.len(),
157                            })
158                        };
159                    }
160                    (inner as *const Value).cast()
161                }
162                None => std::ptr::null(),
163            },
164            _ => std::ptr::null(),
165        }
166    })
167}
168
169/// The number a payload carries, as a double: a float as it is, an
170/// integer widened. The reader for everything geometry-shaped an event
171/// carries - a drag's `x`/`dx`, a layout's rect, a resize's `scale` -
172/// which kui_value_as_int would truncate. False for anything else.
173#[unsafe(no_mangle)]
174pub extern "C" fn kui_value_as_float(v: *const KuiValue, out: *mut f64) -> bool {
175    guard(false, || {
176        let (Some(v), Some(out)) = (unsafe { v.as_ref() }, unsafe { out.as_mut() }) else {
177            return false;
178        };
179        match v.0.as_float() {
180            Some(f) => {
181                *out = f;
182                true
183            }
184            None => false,
185        }
186    })
187}
188
189/// A payload's boolean - a key event's `shift` / `ctrl` / `alt` / `super`
190/// / `repeat`, a modifiers event's four. False - and `*out` untouched -
191/// for anything that is not one; a bool is never coerced from a number.
192#[unsafe(no_mangle)]
193pub extern "C" fn kui_value_as_bool(v: *const KuiValue, out: *mut bool) -> bool {
194    guard(false, || {
195        let (Some(v), Some(out)) = (unsafe { v.as_ref() }, unsafe { out.as_mut() }) else {
196            return false;
197        };
198        match v.0.as_bool() {
199            Some(b) => {
200                *out = b;
201                true
202            }
203            None => false,
204        }
205    })
206}
207
208/// Whether the value is the null one: what a key event's `text` is on a
209/// release, and a missing `tag`. A NULL pointer answers true too, so a
210/// `kui_value_get` miss reads the same as an explicit null.
211#[unsafe(no_mangle)]
212pub extern "C" fn kui_value_is_null(v: *const KuiValue) -> bool {
213    guard(true, || match unsafe { v.as_ref() } {
214        Some(v) => matches!(v.0, Value::Null),
215        None => true,
216    })
217}
218
219/// The integer a value holds; false for anything that is not an integer
220/// (a float is not coerced; see [`kui_value_as_float`]).
221#[unsafe(no_mangle)]
222pub extern "C" fn kui_value_as_int(v: *const KuiValue, out: *mut i64) -> bool {
223    guard(false, || {
224        let (Some(v), Some(out)) = (unsafe { v.as_ref() }, unsafe { out.as_mut() }) else {
225            return false;
226        };
227        match v.0.as_int() {
228            Some(i) => {
229                *out = i;
230                true
231            }
232            None => false,
233        }
234    })
235}
236
237/// Borrowed string view; valid as long as the value.
238#[unsafe(no_mangle)]
239pub extern "C" fn kui_value_as_str(v: *const KuiValue, out: *mut KuiStr) -> bool {
240    guard(false, || {
241        let (Some(v), Some(out)) = (unsafe { v.as_ref() }, unsafe { out.as_mut() }) else {
242            return false;
243        };
244        match v.0.as_str() {
245            Some(s) => {
246                *out = KuiStr {
247                    ptr: s.as_ptr(),
248                    len: s.len(),
249                };
250                true
251            }
252            None => false,
253        }
254    })
255}
256
257/// Frees a value the caller owns. Never call it on a borrowed value (an
258/// event's payload, a `kui_value_get` result); NULL is a no-op.
259#[unsafe(no_mangle)]
260pub extern "C" fn kui_value_free(v: *mut KuiValue) {
261    if !v.is_null() {
262        drop(unsafe { Box::from_raw(v) });
263    }
264}