kdb/
dictionary.rs

1use crate::list::List;
2use crate::{any::Any, k::K};
3use crate::{k_type::MIXED_LIST, kapi, type_traits::KObject};
4use crate::{
5    k_type::{KTypeCode, DICT},
6    kbox::KBox,
7    type_traits::KTyped,
8};
9use std::{mem, ops::Index};
10
11/// A key value based dictionary.
12#[repr(transparent)]
13pub struct Dictionary {
14    k: K,
15}
16
17impl Dictionary {
18    fn key_list_mut(&mut self) -> &mut KBox<List<Any>> {
19        unsafe { &mut *(&mut self.k.union.dict.k as *mut _ as *mut KBox<List<Any>>) }
20    }
21
22    fn value_list_mut(&mut self) -> &mut KBox<List<Any>> {
23        unsafe { &mut *(&mut self.k.union.dict.v as *mut _ as *mut KBox<List<Any>>) }
24    }
25
26    fn key_list(&self) -> &KBox<List<Any>> {
27        unsafe { &*(&self.k.union.dict.k as *const _ as *const KBox<List<Any>>) }
28    }
29
30    fn value_list(&self) -> &KBox<List<Any>> {
31        unsafe { &*(&self.k.union.dict.v as *const _ as *const KBox<List<Any>>) }
32    }
33
34    /// The number of items in the dictionary.
35    #[inline]
36    pub fn len(&self) -> usize {
37        self.key_list().len()
38    }
39
40    /// Returns true if the dictionary has no items.
41    #[inline]
42    pub fn is_empty(&self) -> bool {
43        self.len() == 0
44    }
45
46    /// Gets a slice containing all the keys in this dictionary.
47    #[inline]
48    pub fn keys(&self) -> &[KBox<Any>] {
49        &self.key_list()[..]
50    }
51
52    /// Gets a slice containing all the values in this dictionary.
53    #[inline]
54    pub fn values(&self) -> &[KBox<Any>] {
55        &self.value_list()[..]
56    }
57
58    /// Insert a specified key and value at the end of the dictionary.
59    /// No checks are done on uniqueness so duplicates are possible.
60    #[inline]
61    pub fn insert(&mut self, key: impl Into<KBox<Any>>, value: impl Into<KBox<Any>>) {
62        self.key_list_mut().push(key.into());
63        self.value_list_mut().push(value.into());
64    }
65
66    /// Gets a value by key. Note that KDB dictionaries are treated as unordered and hence this is an O(n) operation.
67    #[inline]
68    pub fn get<T: Into<KBox<Any>>>(&self, key: T) -> Option<&KBox<Any>> {
69        let key = key.into();
70        let index = self
71            .keys()
72            .iter()
73            .enumerate()
74            .find(|(_, k2)| unsafe { *k2.k.as_ptr() == *key.k.as_ptr() })
75            .map(|(i, _)| i)?;
76        self.values().get(index)
77    }
78
79    /// An iterator through every value in the KDB object
80    #[inline]
81    pub fn iter(&self) -> impl Iterator<Item = (&KBox<Any>, &KBox<Any>)> {
82        self.keys().iter().zip(self.values().iter())
83    }
84}
85
86impl<T> Index<T> for Dictionary
87where
88    for<'a> T: Into<KBox<Any>>,
89{
90    type Output = Any;
91
92    fn index(&self, index: T) -> &Self::Output {
93        self.get(index).unwrap()
94    }
95}
96
97impl KObject for Dictionary {
98    #[inline]
99    fn k_ptr(&self) -> *const K {
100        &self.k
101    }
102
103    #[inline]
104    fn k_ptr_mut(&mut self) -> *mut K {
105        &mut self.k
106    }
107}
108
109impl KTyped for Dictionary {
110    const K_TYPE: KTypeCode = DICT;
111}
112
113impl KBox<Dictionary> {
114    /// Create a new empty dictionary.
115    pub fn new_dict() -> Self {
116        unsafe {
117            let keys = kapi::ktn(MIXED_LIST.into(), 0) as *mut K;
118            let values = kapi::ktn(MIXED_LIST.into(), 0) as *mut K;
119            mem::transmute(kapi::xD(keys, values))
120        }
121    }
122}
123
124#[cfg(test)]
125mod tests {
126    use crate::symbol;
127
128    use super::*;
129
130    #[test]
131    fn insert_appends_items_to_dictionary() {
132        let mut dict = KBox::new_dict();
133        dict.insert(symbol("Hello"), symbol("World"));
134
135        assert_eq!(dict.len(), 1);
136    }
137
138    #[test]
139    fn get_retrieves_items_by_key() {
140        let mut dict = KBox::new_dict();
141        dict.insert(symbol("Hello"), symbol("World"));
142
143        let val = dict.get(symbol("Hello")).unwrap();
144
145        assert_eq!(*val.as_ref(), *KBox::<Any>::from(symbol("World")).as_ref());
146    }
147}