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 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 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
186pub 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}