1use super::Expression;
2use crate::Span;
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 },
18 Int {
20 val: usize,
21 span: Span,
22 optional: bool,
25 },
26}
27
28impl PathMember {
29 pub fn int(val: usize, optional: bool, span: Span) -> Self {
30 PathMember::Int {
31 val,
32 span,
33 optional,
34 }
35 }
36
37 pub fn string(val: String, optional: bool, span: Span) -> Self {
38 PathMember::String {
39 val,
40 span,
41 optional,
42 }
43 }
44
45 pub fn test_int(val: usize, optional: bool) -> Self {
46 PathMember::Int {
47 val,
48 optional,
49 span: Span::test_data(),
50 }
51 }
52
53 pub fn test_string(val: String, optional: bool) -> Self {
54 PathMember::String {
55 val,
56 optional,
57 span: Span::test_data(),
58 }
59 }
60
61 pub fn make_optional(&mut self) {
62 match self {
63 PathMember::String {
64 ref mut optional, ..
65 } => *optional = true,
66 PathMember::Int {
67 ref mut optional, ..
68 } => *optional = true,
69 }
70 }
71
72 pub fn span(&self) -> Span {
73 match self {
74 PathMember::String { span, .. } => *span,
75 PathMember::Int { span, .. } => *span,
76 }
77 }
78}
79
80impl PartialEq for PathMember {
81 fn eq(&self, other: &Self) -> bool {
82 match (self, other) {
83 (
84 Self::String {
85 val: l_val,
86 optional: l_opt,
87 ..
88 },
89 Self::String {
90 val: r_val,
91 optional: r_opt,
92 ..
93 },
94 ) => l_val == r_val && l_opt == r_opt,
95 (
96 Self::Int {
97 val: l_val,
98 optional: l_opt,
99 ..
100 },
101 Self::Int {
102 val: r_val,
103 optional: r_opt,
104 ..
105 },
106 ) => l_val == r_val && l_opt == r_opt,
107 _ => false,
108 }
109 }
110}
111
112impl PartialOrd for PathMember {
113 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
114 match (self, other) {
115 (
116 PathMember::String {
117 val: l_val,
118 optional: l_opt,
119 ..
120 },
121 PathMember::String {
122 val: r_val,
123 optional: r_opt,
124 ..
125 },
126 ) => {
127 let val_ord = Some(l_val.cmp(r_val));
128
129 if let Some(Ordering::Equal) = val_ord {
130 Some(l_opt.cmp(r_opt))
131 } else {
132 val_ord
133 }
134 }
135 (
136 PathMember::Int {
137 val: l_val,
138 optional: l_opt,
139 ..
140 },
141 PathMember::Int {
142 val: r_val,
143 optional: r_opt,
144 ..
145 },
146 ) => {
147 let val_ord = Some(l_val.cmp(r_val));
148
149 if let Some(Ordering::Equal) = val_ord {
150 Some(l_opt.cmp(r_opt))
151 } else {
152 val_ord
153 }
154 }
155 (PathMember::Int { .. }, PathMember::String { .. }) => Some(Ordering::Greater),
156 (PathMember::String { .. }, PathMember::Int { .. }) => Some(Ordering::Less),
157 }
158 }
159}
160
161#[derive(Debug, Clone, PartialEq, PartialOrd, Serialize, Deserialize)]
174pub struct CellPath {
175 pub members: Vec<PathMember>,
176}
177
178impl CellPath {
179 pub fn make_optional(&mut self) {
180 for member in &mut self.members {
181 member.make_optional();
182 }
183 }
184
185 pub fn to_column_name(&self) -> String {
187 let mut s = String::new();
188
189 for member in &self.members {
190 match member {
191 PathMember::Int { val, .. } => {
192 s += &val.to_string();
193 }
194 PathMember::String { val, .. } => {
195 s += val;
196 }
197 }
198
199 s.push('.');
200 }
201
202 s.pop(); s
204 }
205}
206
207impl Display for CellPath {
208 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
209 write!(f, "$")?;
210 for member in self.members.iter() {
211 match member {
212 PathMember::Int { val, optional, .. } => {
213 let question_mark = if *optional { "?" } else { "" };
214 write!(f, ".{val}{question_mark}")?
215 }
216 PathMember::String { val, optional, .. } => {
217 let question_mark = if *optional { "?" } else { "" };
218 let val = if needs_quoting(val) {
219 &escape_quote_string(val)
220 } else {
221 val
222 };
223 write!(f, ".{val}{question_mark}")?
224 }
225 }
226 }
227 Ok(())
228 }
229}
230
231#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
232pub struct FullCellPath {
233 pub head: Expression,
234 pub tail: Vec<PathMember>,
235}
236
237#[cfg(test)]
238mod test {
239 use super::*;
240 use std::cmp::Ordering::Greater;
241
242 #[test]
243 fn path_member_partial_ord() {
244 assert_eq!(
245 Some(Greater),
246 PathMember::test_int(5, true).partial_cmp(&PathMember::test_string("e".into(), true))
247 );
248
249 assert_eq!(
250 Some(Greater),
251 PathMember::test_int(5, true).partial_cmp(&PathMember::test_int(5, false))
252 );
253
254 assert_eq!(
255 Some(Greater),
256 PathMember::test_int(6, true).partial_cmp(&PathMember::test_int(5, true))
257 );
258
259 assert_eq!(
260 Some(Greater),
261 PathMember::test_string("e".into(), true)
262 .partial_cmp(&PathMember::test_string("e".into(), false))
263 );
264
265 assert_eq!(
266 Some(Greater),
267 PathMember::test_string("f".into(), true)
268 .partial_cmp(&PathMember::test_string("e".into(), true))
269 );
270 }
271}