tabnas_alchemy/shared/
selector.rs1use std::fmt;
10
11#[derive(Clone, Debug, PartialEq, Eq, Hash)]
13pub enum Segment {
14 Key(Box<str>),
15 Index(usize),
16}
17
18impl Segment {
19 pub fn key(k: impl Into<Box<str>>) -> Segment {
20 Segment::Key(k.into())
21 }
22}
23
24#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
26pub struct Path(pub Vec<Segment>);
27
28impl Path {
29 pub fn root() -> Path {
30 Path(Vec::new())
31 }
32
33 pub fn push(&mut self, seg: Segment) {
34 self.0.push(seg);
35 }
36
37 pub fn pop(&mut self) -> Option<Segment> {
38 self.0.pop()
39 }
40
41 pub fn depth(&self) -> usize {
42 self.0.len()
43 }
44
45 pub fn segments(&self) -> &[Segment] {
46 &self.0
47 }
48}
49
50pub fn write_key(f: &mut fmt::Formatter<'_>, key: &str) -> fmt::Result {
52 let bare = !key.is_empty()
53 && key
54 .chars()
55 .next()
56 .is_some_and(|c| c.is_ascii_alphabetic() || c == '_')
57 && key.chars().all(|c| c.is_ascii_alphanumeric() || c == '_');
58 if bare {
59 write!(f, ".{key}")
60 } else {
61 write!(f, ".{}", serde_json::Value::String(key.to_string()))
62 }
63}
64
65impl fmt::Display for Path {
66 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67 if self.0.is_empty() {
68 return f.write_str(".");
69 }
70 for seg in &self.0 {
71 match seg {
72 Segment::Key(k) => write_key(f, k)?,
73 Segment::Index(i) => write!(f, "[{i}]")?,
74 }
75 }
76 Ok(())
77 }
78}
79
80#[derive(Clone, Debug, PartialEq, Eq, Hash)]
82pub enum Step {
83 Property(Box<str>),
85 Index(usize),
87 EachIndex,
89 EachMember,
91}
92
93#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
95pub struct Selector(pub Vec<Step>);
96
97impl Selector {
98 pub fn root() -> Selector {
100 Selector(Vec::new())
101 }
102
103 pub fn property(mut self, name: impl Into<Box<str>>) -> Selector {
104 self.0.push(Step::Property(name.into()));
105 self
106 }
107
108 pub fn index(mut self, i: usize) -> Selector {
109 self.0.push(Step::Index(i));
110 self
111 }
112
113 pub fn each_index(mut self) -> Selector {
114 self.0.push(Step::EachIndex);
115 self
116 }
117
118 pub fn each_member(mut self) -> Selector {
119 self.0.push(Step::EachMember);
120 self
121 }
122
123 pub fn compose(mut self, other: &Selector) -> Selector {
125 self.0.extend(other.0.iter().cloned());
126 self
127 }
128
129 pub fn from_segments(segments: &[Segment]) -> Selector {
131 Selector(
132 segments
133 .iter()
134 .map(|s| match s {
135 Segment::Key(k) => Step::Property(k.clone()),
136 Segment::Index(i) => Step::Index(*i),
137 })
138 .collect(),
139 )
140 }
141
142 pub fn steps(&self) -> &[Step] {
143 &self.0
144 }
145
146 pub fn is_root(&self) -> bool {
147 self.0.is_empty()
148 }
149
150 pub fn is_multi(&self) -> bool {
152 self.0
153 .iter()
154 .any(|s| matches!(s, Step::EachIndex | Step::EachMember))
155 }
156
157 pub fn may_overlap(&self, other: &Selector) -> bool {
161 let (short, long) = if self.0.len() <= other.0.len() {
162 (self, other)
163 } else {
164 (other, self)
165 };
166 short
167 .0
168 .iter()
169 .zip(long.0.iter())
170 .all(|(a, b)| step_may_match_same(a, b))
171 }
172}
173
174fn step_may_match_same(a: &Step, b: &Step) -> bool {
175 match (a, b) {
176 (Step::Property(x), Step::Property(y)) => x == y,
177 (Step::Property(_), Step::EachMember) | (Step::EachMember, Step::Property(_)) => true,
178 (Step::EachMember, Step::EachMember) => true,
179 (Step::Index(x), Step::Index(y)) => x == y,
180 (Step::Index(_), Step::EachIndex) | (Step::EachIndex, Step::Index(_)) => true,
181 (Step::EachIndex, Step::EachIndex) => true,
182 _ => false,
183 }
184}
185
186impl fmt::Display for Selector {
187 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
188 if self.0.is_empty() {
189 return f.write_str(".");
190 }
191 for step in &self.0 {
192 match step {
193 Step::Property(k) => write_key(f, k)?,
194 Step::Index(i) => write!(f, "[{i}]")?,
195 Step::EachIndex => f.write_str("[*]")?,
196 Step::EachMember => f.write_str("[]")?,
197 }
198 }
199 Ok(())
200 }
201}
202
203#[cfg(test)]
204mod tests {
205 use super::*;
206
207 #[test]
208 fn display_is_jq() {
209 let s = Selector::root()
210 .property("response")
211 .property("odd key")
212 .index(3)
213 .each_index()
214 .each_member();
215 assert_eq!(s.to_string(), ".response.\"odd key\"[3][*][]");
216 assert_eq!(Selector::root().to_string(), ".");
217 let p = Path(vec![
218 Segment::key("a"),
219 Segment::Index(0),
220 Segment::key("b-c"),
221 ]);
222 assert_eq!(p.to_string(), ".a[0].\"b-c\"");
223 assert_eq!(Path::root().to_string(), ".");
224 }
225
226 #[test]
227 fn from_segments_is_single() {
228 let s = Selector::from_segments(&[Segment::key("account"), Segment::key("balance")]);
229 assert_eq!(s.to_string(), ".account.balance");
230 assert!(!s.is_multi());
231 assert!(Selector::root().each_index().is_multi());
232 }
233
234 #[test]
235 fn overlap() {
236 let rows = Selector::root().property("records").each_index();
237 let meta = Selector::root().property("metadata");
238 let inner = Selector::root().property("records").index(2).property("x");
239 assert!(!rows.may_overlap(&meta));
240 assert!(rows.may_overlap(&inner));
241 assert!(inner.may_overlap(&rows));
242 assert!(rows.may_overlap(&rows));
243 assert!(Selector::root().may_overlap(&meta));
244 assert!(Selector::root().each_member().may_overlap(&meta));
245 assert!(!Selector::root().each_index().may_overlap(&meta));
246 }
247
248 #[test]
249 fn compose_appends() {
250 let a = Selector::root().property("a");
251 let b = Selector::root().each_index();
252 assert_eq!(a.compose(&b).to_string(), ".a[*]");
253 }
254}