ghee_lang/
key.rs

1use std::{
2    ops::{Index, IndexMut},
3    path::PathBuf,
4    slice::Iter,
5};
6
7use anyhow::Result;
8use nom::{character::complete::char, combinator::map, multi::separated_list1, IResult};
9use serde::{Deserialize, Deserializer, Serialize, Serializer};
10use thiserror::Error;
11
12use crate::Record;
13
14use super::{
15    value::Value,
16    xattr::{parse_xattr, StringVisitor, Xattr},
17};
18
19#[derive(Error, Debug)]
20pub enum KeyErr {
21    #[error("The subkey {subkey} had no value on path {path}")]
22    SubkeyValueNotFoundOnPath { subkey: Xattr, path: PathBuf },
23
24    #[error("The subkey {subkey} had no value in record {record:?}")]
25    SubkeyValueNotFoundInRecord { subkey: Xattr, record: Record },
26
27    #[error("Record must be JSON object")]
28    RecordNotObject,
29}
30
31#[derive(Clone, Debug, PartialEq, Eq, Ord, PartialOrd)]
32pub struct Key {
33    pub subkeys: Vec<Xattr>,
34}
35
36impl Key {
37    pub fn new(subkeys: Vec<Xattr>) -> Self {
38        Self { subkeys }
39    }
40
41    pub fn from_string<S: ToString>(s: S) -> Self {
42        Self::from(s.to_string().split(","))
43    }
44
45    pub fn is_empty(&self) -> bool {
46        self.subkeys.is_empty()
47    }
48
49    pub fn len(&self) -> usize {
50        self.subkeys.len()
51    }
52
53    pub fn to_string(&self) -> String {
54        if self.subkeys.is_empty() {
55            return String::from("");
56        }
57        // subkeys.len() - 1 is added to account for the commas
58        let len = self
59            .subkeys
60            .iter()
61            .map(|k| k.len())
62            .reduce(|acc, e| acc + e)
63            .unwrap_or_default()
64            + self.subkeys.len()
65            - 1;
66
67        let mut s = String::with_capacity(len);
68
69        for (i, k) in self.subkeys.iter().enumerate() {
70            if i > 0 {
71                s.push(',');
72            }
73            s.push_str(k.to_string().as_str());
74        }
75
76        s
77    }
78
79    pub fn to_minimal_string(&self) -> String {
80        if self.subkeys.is_empty() {
81            return String::from("");
82        }
83        // subkeys.len() - 1 is added to account for the commas
84        let len = self
85            .subkeys
86            .iter()
87            .map(|k| k.minimal_len())
88            .reduce(|acc, e| acc + e)
89            .unwrap_or_default()
90            + self.subkeys.len()
91            - 1;
92
93        let mut s = String::with_capacity(len);
94
95        for (i, k) in self.subkeys.iter().enumerate() {
96            if i > 0 {
97                s.push(',');
98            }
99            s.push_str(k.to_minimal_string().as_str());
100        }
101
102        s
103    }
104
105    pub fn iter(&self) -> Iter<Xattr> {
106        self.subkeys.iter()
107    }
108
109    pub fn value_for_record(&self, record: &Record) -> Result<Vec<Value>> {
110        let mut values: Vec<Value> = Vec::with_capacity(self.subkeys.len());
111
112        for subkey in self.subkeys.iter() {
113            let value = record
114                .get(&subkey)
115                .ok_or_else(|| KeyErr::SubkeyValueNotFoundInRecord {
116                    subkey: subkey.clone(),
117                    record: record.clone(),
118                })?
119                .clone();
120
121            values.push(value);
122        }
123
124        Ok(values)
125    }
126
127    pub fn path_for_record(&self, mut base_path: PathBuf, record: &Record) -> Result<PathBuf> {
128        let parts = self.value_for_record(record)?;
129
130        for part in parts {
131            base_path.push(part.to_string());
132        }
133
134        Ok(base_path)
135    }
136}
137
138impl<S: ToString, I: IntoIterator<Item = S>> From<I> for Key {
139    fn from(keys: I) -> Self {
140        let xattrs: Vec<Xattr> = keys
141            .into_iter()
142            .map(|k| k.to_string().into_bytes())
143            .map(|b| {
144                let (remainder, xattr) = parse_xattr(b.as_slice()).unwrap();
145                debug_assert_eq!(remainder.len(), 0);
146                xattr
147            })
148            .collect();
149
150        Self::new(xattrs)
151    }
152}
153
154impl Index<usize> for Key {
155    type Output = Xattr;
156    fn index(&self, index: usize) -> &Self::Output {
157        &self.subkeys[index]
158    }
159}
160
161impl IndexMut<usize> for Key {
162    fn index_mut(&mut self, index: usize) -> &mut Self::Output {
163        &mut self.subkeys[index]
164    }
165}
166
167impl Serialize for Key {
168    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
169    where
170        S: Serializer,
171    {
172        serializer.serialize_str(self.to_minimal_string().as_str())
173    }
174}
175
176impl<'a> Deserialize<'a> for Key {
177    fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
178    where
179        D: Deserializer<'a>,
180    {
181        let s = deserializer.deserialize_string(StringVisitor).unwrap();
182        Ok(Self::from_string(s))
183    }
184}
185
186/// A primary key, possibly compound
187pub fn parse_key(i: &[u8]) -> IResult<&[u8], Key> {
188    map(separated_list1(char(' '), parse_xattr), Key::new)(i)
189}
190
191#[cfg(test)]
192mod test {
193    use crate::{xattr::parse_xattr, Key};
194
195    #[test]
196    fn test_from_string_iter() {
197        assert_eq!(
198            Key::from(vec!["a", "b", "system.c"]),
199            Key::new(vec![
200                parse_xattr(b"a").unwrap().1,
201                parse_xattr(b"b").unwrap().1,
202                parse_xattr(b"system.c").unwrap().1
203            ])
204        );
205    }
206}