1use super::Expression;
2use crate::{Span, casing::Casing};
3use nu_utils::{escape_quote_string, needs_quoting};
4use serde::{Deserialize, Serialize};
5use std::{cmp::Ordering, fmt::Display};
6
7#[derive(Debug, Clone, Serialize, Deserialize)]
9pub enum PathMember {
10 String {
12 val: String,
13 span: Span,
14 optional: bool,
17 casing: Casing,
19 },
20 Int {
22 val: usize,
23 span: Span,
24 optional: bool,
27 },
28}
29
30impl PathMember {
31 pub fn int(val: usize, optional: bool, span: Span) -> Self {
32 PathMember::Int {
33 val,
34 span,
35 optional,
36 }
37 }
38
39 pub fn string(val: String, optional: bool, casing: Casing, span: Span) -> Self {
40 PathMember::String {
41 val,
42 span,
43 optional,
44 casing,
45 }
46 }
47
48 pub fn test_int(val: usize, optional: bool) -> Self {
49 PathMember::Int {
50 val,
51 optional,
52 span: Span::test_data(),
53 }
54 }
55
56 pub fn test_string(val: String, optional: bool, casing: Casing) -> Self {
57 PathMember::String {
58 val,
59 optional,
60 casing,
61 span: Span::test_data(),
62 }
63 }
64
65 pub fn make_optional(&mut self) {
66 match self {
67 PathMember::String { optional, .. } => *optional = true,
68 PathMember::Int { optional, .. } => *optional = true,
69 }
70 }
71
72 pub fn make_insensitive(&mut self) {
73 match self {
74 PathMember::String { casing, .. } => *casing = Casing::Insensitive,
75 PathMember::Int { .. } => {}
76 }
77 }
78
79 pub fn span(&self) -> Span {
80 match self {
81 PathMember::String { span, .. } => *span,
82 PathMember::Int { span, .. } => *span,
83 }
84 }
85
86 pub fn memory_size(&self) -> usize {
88 match self {
89 PathMember::String { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
90 PathMember::Int { .. } => std::mem::size_of::<Self>(),
91 }
92 }
93}
94
95impl PartialEq for PathMember {
96 fn eq(&self, other: &Self) -> bool {
97 match (self, other) {
98 (
99 Self::String {
100 val: l_val,
101 optional: l_opt,
102 ..
103 },
104 Self::String {
105 val: r_val,
106 optional: r_opt,
107 ..
108 },
109 ) => l_val == r_val && l_opt == r_opt,
110 (
111 Self::Int {
112 val: l_val,
113 optional: l_opt,
114 ..
115 },
116 Self::Int {
117 val: r_val,
118 optional: r_opt,
119 ..
120 },
121 ) => l_val == r_val && l_opt == r_opt,
122 _ => false,
123 }
124 }
125}
126
127impl PartialOrd for PathMember {
128 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
129 match (self, other) {
130 (
131 PathMember::String {
132 val: l_val,
133 optional: l_opt,
134 ..
135 },
136 PathMember::String {
137 val: r_val,
138 optional: r_opt,
139 ..
140 },
141 ) => {
142 let val_ord = Some(l_val.cmp(r_val));
143
144 if let Some(Ordering::Equal) = val_ord {
145 Some(l_opt.cmp(r_opt))
146 } else {
147 val_ord
148 }
149 }
150 (
151 PathMember::Int {
152 val: l_val,
153 optional: l_opt,
154 ..
155 },
156 PathMember::Int {
157 val: r_val,
158 optional: r_opt,
159 ..
160 },
161 ) => {
162 let val_ord = Some(l_val.cmp(r_val));
163
164 if let Some(Ordering::Equal) = val_ord {
165 Some(l_opt.cmp(r_opt))
166 } else {
167 val_ord
168 }
169 }
170 (PathMember::Int { .. }, PathMember::String { .. }) => Some(Ordering::Greater),
171 (PathMember::String { .. }, PathMember::Int { .. }) => Some(Ordering::Less),
172 }
173 }
174}
175
176#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
189pub struct CellPath {
190 pub members: Vec<PathMember>,
191}
192
193impl CellPath {
194 pub fn make_optional(&mut self) {
195 for member in &mut self.members {
196 member.make_optional();
197 }
198 }
199
200 pub fn make_insensitive(&mut self) {
201 for member in &mut self.members {
202 member.make_insensitive();
203 }
204 }
205
206 pub fn to_column_name(&self) -> String {
208 let mut s = String::new();
209
210 for member in &self.members {
211 match member {
212 PathMember::Int { val, .. } => {
213 s += &val.to_string();
214 }
215 PathMember::String { val, .. } => {
216 s += val;
217 }
218 }
219
220 s.push('.');
221 }
222
223 s.pop(); s
225 }
226
227 pub fn memory_size(&self) -> usize {
229 std::mem::size_of::<Self>() + self.members.iter().map(|m| m.memory_size()).sum::<usize>()
230 }
231}
232
233impl Display for CellPath {
234 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
235 write!(f, "$")?;
236 for member in self.members.iter() {
237 match member {
238 PathMember::Int { val, optional, .. } => {
239 let question_mark = if *optional { "?" } else { "" };
240 write!(f, ".{val}{question_mark}")?
241 }
242 PathMember::String {
243 val,
244 optional,
245 casing,
246 ..
247 } => {
248 let question_mark = if *optional { "?" } else { "" };
249 let exclamation_mark = if *casing == Casing::Insensitive {
250 "!"
251 } else {
252 ""
253 };
254 let val = if needs_quoting(val) {
255 &escape_quote_string(val)
256 } else {
257 val
258 };
259 write!(f, ".{val}{exclamation_mark}{question_mark}")?
260 }
261 }
262 }
263 if self.members.is_empty() {
265 write!(f, ".")?;
266 }
267 Ok(())
268 }
269}
270
271#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
272pub struct FullCellPath {
273 pub head: Expression,
274 pub tail: Vec<PathMember>,
275}
276
277#[cfg(test)]
278mod test {
279 use super::*;
280 use std::cmp::Ordering::Greater;
281
282 #[test]
283 fn path_member_partial_ord() {
284 assert_eq!(
285 Some(Greater),
286 PathMember::test_int(5, true).partial_cmp(&PathMember::test_string(
287 "e".into(),
288 true,
289 Casing::Sensitive
290 ))
291 );
292
293 assert_eq!(
294 Some(Greater),
295 PathMember::test_int(5, true).partial_cmp(&PathMember::test_int(5, false))
296 );
297
298 assert_eq!(
299 Some(Greater),
300 PathMember::test_int(6, true).partial_cmp(&PathMember::test_int(5, true))
301 );
302
303 assert_eq!(
304 Some(Greater),
305 PathMember::test_string("e".into(), true, Casing::Sensitive).partial_cmp(
306 &PathMember::test_string("e".into(), false, Casing::Sensitive)
307 )
308 );
309
310 assert_eq!(
311 Some(Greater),
312 PathMember::test_string("f".into(), true, Casing::Sensitive).partial_cmp(
313 &PathMember::test_string("e".into(), true, Casing::Sensitive)
314 )
315 );
316 }
317}