Skip to main content

gg_sdk/
object.rs

1// aws-greengrass-component-sdk - Lightweight AWS IoT Greengrass SDK
2// Copyright Amazon.com, Inc. or its affiliates. All Rights Reserved.
3// SPDX-License-Identifier: Apache-2.0
4
5use core::{
6    fmt, marker::PhantomData, ops::Deref, ptr, ptr::NonNull, slice, str,
7};
8
9use crate::c;
10
11/// A generic object.
12#[derive(Copy, Clone)]
13#[repr(transparent)]
14pub struct Object<'a> {
15    c: c::GgObject,
16    phantom: PhantomData<UnpackedObject<'a>>,
17}
18
19impl fmt::Debug for Object<'_> {
20    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
21        self.unpack().fmt(f)
22    }
23}
24
25/// Unpacked object value.
26#[derive(Debug)]
27#[repr(u8)]
28pub enum UnpackedObject<'a> {
29    /// Null value.
30    Null = 0,
31    /// Boolean value.
32    Bool(bool) = 1,
33    /// Signed 64-bit integer.
34    I64(i64) = 2,
35    /// 64-bit floating point.
36    F64(f64) = 3,
37    /// UTF-8 string buffer.
38    Buf(&'a str) = 4,
39    /// List of objects.
40    List(List<'a>) = 5,
41    /// Map of key-value pairs.
42    Map(Map<'a>) = 6,
43}
44
45impl<'a> Object<'a> {
46    /// Null object constant.
47    pub const NULL: Self = Self {
48        c: c::GgObject { _private: [0; _] },
49        phantom: PhantomData,
50    };
51
52    /// Create a boolean reference.
53    ///
54    /// # Examples
55    ///
56    /// ```
57    /// use gg_sdk::{Object, UnpackedObject};
58    ///
59    /// let obj = Object::bool(false);
60    /// assert!(matches!(obj.unpack(), UnpackedObject::Bool(false)));
61    /// ```
62    #[must_use]
63    pub fn bool(b: bool) -> Self {
64        Self {
65            c: unsafe { c::gg_obj_bool(b) },
66            phantom: PhantomData,
67        }
68    }
69
70    /// Create a signed integer reference.
71    #[must_use]
72    pub fn i64(i: i64) -> Self {
73        Self {
74            c: unsafe { c::gg_obj_i64(i) },
75            phantom: PhantomData,
76        }
77    }
78
79    /// Create a floating point reference.
80    #[must_use]
81    pub fn f64(f: f64) -> Self {
82        Self {
83            c: unsafe { c::gg_obj_f64(f) },
84            phantom: PhantomData,
85        }
86    }
87
88    /// Create a string buffer reference.
89    ///
90    /// # Examples
91    ///
92    /// ```
93    /// use gg_sdk::{Object, UnpackedObject};
94    ///
95    /// let s = "borrowed";
96    /// let obj = Object::buf(s);
97    /// assert!(matches!(obj.unpack(), UnpackedObject::Buf("borrowed")));
98    /// ```
99    #[must_use]
100    pub fn buf(buf: &'a str) -> Self {
101        Self {
102            c: unsafe { c::gg_obj_buf(buf.into()) },
103            phantom: PhantomData,
104        }
105    }
106
107    /// Create a list reference.
108    ///
109    /// # Examples
110    ///
111    /// ```
112    /// use gg_sdk::{Object, UnpackedObject};
113    ///
114    /// let items = [Object::i64(1), Object::i64(2)];
115    /// let obj = Object::list(&items[..]);
116    /// if let UnpackedObject::List(list) = obj.unpack() {
117    ///     assert_eq!(list.len(), 2);
118    /// }
119    /// ```
120    #[must_use]
121    pub fn list(list: &'a [Object<'a>]) -> Self {
122        Self {
123            c: unsafe { c::gg_obj_list(list.into()) },
124            phantom: PhantomData,
125        }
126    }
127
128    /// Create a map reference.
129    ///
130    /// # Examples
131    ///
132    /// ```
133    /// use gg_sdk::{Kv, Object, UnpackedObject};
134    ///
135    /// let pairs = [Kv::new("k", Object::i64(1))];
136    /// let obj = Object::map(&pairs[..]);
137    /// if let UnpackedObject::Map(m) = obj.unpack() {
138    ///     assert_eq!(m.len(), 1);
139    /// }
140    /// ```
141    #[must_use]
142    pub fn map(map: &'a [Kv<'a>]) -> Self {
143        Self {
144            c: unsafe { c::gg_obj_map(map.into()) },
145            phantom: PhantomData,
146        }
147    }
148}
149
150unsafe fn slice_from_c<'a, T>(ptr: *const T, len: usize) -> &'a [T] {
151    let ptr = NonNull::new(ptr.cast_mut()).unwrap_or_else(|| {
152        assert_eq!(len, 0, "null pointer with non-zero length");
153        NonNull::dangling()
154    });
155    unsafe { NonNull::slice_from_raw_parts(ptr, len).as_ref() }
156}
157
158impl<'a> Object<'a> {
159    /// Unpack the object to inspect its value.
160    ///
161    /// # Examples
162    ///
163    /// ```
164    /// use gg_sdk::{Kv, Object, UnpackedObject};
165    ///
166    /// let obj = Object::i64(42);
167    /// match obj.unpack() {
168    ///     UnpackedObject::Null => println!("null"),
169    ///     UnpackedObject::Bool(b) => println!("bool: {}", b),
170    ///     UnpackedObject::I64(n) => assert_eq!(n, 42),
171    ///     UnpackedObject::F64(f) => println!("float: {}", f),
172    ///     UnpackedObject::Buf(s) => println!("string: {}", s),
173    ///     UnpackedObject::List(items) => println!("list of {} items", items.len()),
174    ///     UnpackedObject::Map(pairs) => println!("map with {} pairs", pairs.len()),
175    /// }
176    ///
177    /// let items = [Object::i64(1), Object::buf("two")];
178    /// let list = Object::list(&items[..]);
179    /// if let UnpackedObject::List(items) = list.unpack() {
180    ///     assert_eq!(items.len(), 2);
181    /// }
182    ///
183    /// let pairs = [Kv::new("key", Object::bool(true))];
184    /// let map = Object::map(&pairs[..]);
185    /// if let UnpackedObject::Map(pairs) = map.unpack() {
186    ///     assert_eq!(pairs[0].key(), "key");
187    /// }
188    /// ```
189    #[must_use]
190    pub fn unpack(&self) -> UnpackedObject<'_> {
191        use c::GgObjectType::*;
192
193        unsafe {
194            match c::gg_obj_type(self.c) {
195                GG_TYPE_NULL => UnpackedObject::Null,
196                GG_TYPE_BOOLEAN => {
197                    UnpackedObject::Bool(c::gg_obj_into_bool(self.c))
198                }
199                GG_TYPE_I64 => UnpackedObject::I64(c::gg_obj_into_i64(self.c)),
200                GG_TYPE_F64 => UnpackedObject::F64(c::gg_obj_into_f64(self.c)),
201                GG_TYPE_BUF => {
202                    let buf = c::gg_obj_into_buf(self.c);
203                    let ptr = slice_from_c(buf.data, buf.len);
204                    UnpackedObject::Buf(str::from_utf8_unchecked(ptr))
205                }
206                GG_TYPE_LIST => {
207                    let list = c::gg_obj_into_list(self.c);
208                    UnpackedObject::List(List(slice_from_c(
209                        list.items.cast::<Object<'a>>(),
210                        list.len,
211                    )))
212                }
213                GG_TYPE_MAP => {
214                    let map = c::gg_obj_into_map(self.c);
215                    UnpackedObject::Map(Map(slice_from_c(
216                        map.pairs.cast::<Kv<'a>>(),
217                        map.len,
218                    )))
219                }
220            }
221        }
222    }
223}
224
225impl Default for Object<'_> {
226    fn default() -> Self {
227        Self::NULL
228    }
229}
230
231impl From<bool> for Object<'_> {
232    fn from(b: bool) -> Self {
233        Self::bool(b)
234    }
235}
236
237impl From<i64> for Object<'_> {
238    fn from(i: i64) -> Self {
239        Self::i64(i)
240    }
241}
242
243impl From<f64> for Object<'_> {
244    fn from(f: f64) -> Self {
245        Self::f64(f)
246    }
247}
248
249impl<'a> From<&'a str> for Object<'a> {
250    fn from(s: &'a str) -> Self {
251        Self::buf(s)
252    }
253}
254
255impl<'a> From<&'a [Object<'a>]> for Object<'a> {
256    fn from(list: &'a [Object<'a>]) -> Self {
257        Self::list(list)
258    }
259}
260
261impl<'a> From<&'a [Kv<'a>]> for Object<'a> {
262    fn from(map: &'a [Kv<'a>]) -> Self {
263        Self::map(map)
264    }
265}
266
267impl<'a> From<Map<'a>> for Object<'a> {
268    fn from(map: Map<'a>) -> Self {
269        Self::map(map.0)
270    }
271}
272
273impl<'a> From<List<'a>> for Object<'a> {
274    fn from(list: List<'a>) -> Self {
275        Self::list(list.0)
276    }
277}
278
279/// A key-value pair.
280#[derive(Copy, Clone)]
281#[repr(transparent)]
282pub struct Kv<'a> {
283    c: c::GgKV,
284    phantom_key: PhantomData<&'a str>,
285    phantom_value: PhantomData<Object<'a>>,
286}
287
288impl fmt::Debug for Kv<'_> {
289    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
290        f.debug_struct("Kv")
291            .field("key", &self.key())
292            .field("val", self.val())
293            .finish()
294    }
295}
296
297impl<'a> Kv<'a> {
298    /// Create a key-value pair.
299    ///
300    /// # Examples
301    ///
302    /// ```
303    /// use gg_sdk::{Kv, Object};
304    ///
305    /// let kv = Kv::new("key", Object::i64(10));
306    /// assert_eq!(kv.key(), "key");
307    /// ```
308    #[must_use]
309    pub fn new(key: &'a str, value: Object<'a>) -> Self {
310        Self {
311            c: unsafe { c::gg_kv(key.into(), value.c) },
312            phantom_key: PhantomData,
313            phantom_value: PhantomData,
314        }
315    }
316
317    /// # Panics
318    /// Panics if the key is not valid UTF-8.
319    #[must_use]
320    pub fn key(&self) -> &str {
321        let buf = unsafe { c::gg_kv_key(self.c) };
322        unsafe {
323            str::from_utf8_unchecked(slice::from_raw_parts(buf.data, buf.len))
324        }
325    }
326
327    /// Get a reference to the value.
328    ///
329    /// # Examples
330    ///
331    /// ```
332    /// use gg_sdk::{Kv, Object, UnpackedObject};
333    ///
334    /// let kv = Kv::new("key", Object::i64(100));
335    /// assert!(matches!(kv.val().unpack(), UnpackedObject::I64(100)));
336    /// ```
337    #[must_use]
338    pub fn val(&self) -> &Object<'a> {
339        unsafe {
340            c::gg_kv_val((&raw const self.c).cast_mut())
341                .cast::<Object>()
342                .as_ref()
343                .unwrap_unchecked()
344        }
345    }
346}
347
348impl<'a> From<Map<'a>> for &'a [Kv<'a>] {
349    fn from(map: Map<'a>) -> Self {
350        map.0
351    }
352}
353
354impl<'a> From<List<'a>> for &'a [Object<'a>] {
355    fn from(list: List<'a>) -> Self {
356        list.0
357    }
358}
359
360/// A map of UTF-8 strings to objects.
361#[derive(Debug, Clone, Copy)]
362#[repr(transparent)]
363pub struct Map<'a>(pub &'a [Kv<'a>]);
364
365/// An array of objects.
366#[derive(Debug, Clone, Copy)]
367#[repr(transparent)]
368pub struct List<'a>(pub &'a [Object<'a>]);
369
370impl Map<'_> {
371    /// Get a value from a map by key.
372    ///
373    /// # Examples
374    ///
375    /// ```
376    /// use gg_sdk::{Kv, Map, Object, UnpackedObject};
377    ///
378    /// let pairs = [
379    ///     Kv::new("a", Object::i64(1)),
380    ///     Kv::new("b", Object::i64(2)),
381    /// ];
382    /// let map: Map = pairs.as_slice().into();
383    /// let val = map.get("b").unwrap();
384    /// assert!(matches!(val.unpack(), UnpackedObject::I64(2)));
385    /// ```
386    #[must_use]
387    pub fn get(&self, key: &str) -> Option<&Object<'_>> {
388        let map: c::GgMap = self.0.into();
389        let mut result: *mut c::GgObject = ptr::null_mut();
390        if unsafe { c::gg_map_get(map, key.into(), &raw mut result) } {
391            Some(unsafe {
392                NonNull::new(result.cast::<Object>())
393                    .unwrap_unchecked()
394                    .as_ref()
395            })
396        } else {
397            None
398        }
399    }
400}
401
402impl<'a> From<&'a [Kv<'a>]> for Map<'a> {
403    fn from(slice: &'a [Kv<'a>]) -> Self {
404        Map(slice)
405    }
406}
407
408impl From<&[Kv<'_>]> for c::GgMap {
409    fn from(kvs: &[Kv<'_>]) -> Self {
410        Self {
411            pairs: kvs.as_ptr().cast_mut().cast::<c::GgKV>(),
412            len: kvs.len(),
413        }
414    }
415}
416
417impl<'a> From<&'a [Object<'a>]> for List<'a> {
418    fn from(slice: &'a [Object<'a>]) -> Self {
419        List(slice)
420    }
421}
422
423impl From<&[Object<'_>]> for c::GgList {
424    fn from(objs: &[Object<'_>]) -> Self {
425        Self {
426            items: objs.as_ptr().cast_mut().cast::<c::GgObject>(),
427            len: objs.len(),
428        }
429    }
430}
431
432impl<'a> Deref for Map<'a> {
433    type Target = [Kv<'a>];
434    fn deref(&self) -> &[Kv<'a>] {
435        self.0
436    }
437}
438
439impl<'a> Deref for List<'a> {
440    type Target = [Object<'a>];
441    fn deref(&self) -> &[Object<'a>] {
442        self.0
443    }
444}