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, str::FromStr};
6use winnow::Parser;
7
8#[derive(Debug, Clone)]
10pub enum PathMember {
11 String {
13 val: String,
14 span: Span,
15 optional: bool,
18 casing: Casing,
20 },
21 Int {
23 val: usize,
24 span: Span,
25 optional: bool,
28 },
29}
30
31impl PathMember {
32 pub fn int(val: usize, optional: bool, span: Span) -> Self {
33 PathMember::Int {
34 val,
35 span,
36 optional,
37 }
38 }
39
40 pub fn string(val: String, optional: bool, casing: Casing, span: Span) -> Self {
41 PathMember::String {
42 val,
43 span,
44 optional,
45 casing,
46 }
47 }
48
49 pub fn test_int(val: usize, optional: bool) -> Self {
50 PathMember::Int {
51 val,
52 optional,
53 span: Span::test_data(),
54 }
55 }
56
57 pub fn test_string(val: impl Into<String>, optional: bool, casing: Casing) -> Self {
58 PathMember::String {
59 val: val.into(),
60 optional,
61 casing,
62 span: Span::test_data(),
63 }
64 }
65
66 pub fn make_optional(&mut self) {
67 match self {
68 PathMember::String { optional, .. } => *optional = true,
69 PathMember::Int { optional, .. } => *optional = true,
70 }
71 }
72
73 pub fn make_insensitive(&mut self) {
74 match self {
75 PathMember::String { casing, .. } => *casing = Casing::Insensitive,
76 PathMember::Int { .. } => {}
77 }
78 }
79
80 pub fn span(&self) -> Span {
81 match self {
82 PathMember::String { span, .. } => *span,
83 PathMember::Int { span, .. } => *span,
84 }
85 }
86
87 pub fn fallback_span(&mut self, span: Span) -> Span {
89 let fallback = span;
90 match self {
91 PathMember::String { span, .. } => span.fallback(fallback),
92 PathMember::Int { span, .. } => span.fallback(fallback),
93 }
94 }
95
96 pub fn memory_size(&self) -> usize {
98 match self {
99 PathMember::String { val, .. } => std::mem::size_of::<Self>() + val.capacity(),
100 PathMember::Int { .. } => std::mem::size_of::<Self>(),
101 }
102 }
103}
104
105impl PartialEq for PathMember {
106 fn eq(&self, other: &Self) -> bool {
107 match (self, other) {
108 (
109 Self::String {
110 val: l_val,
111 optional: l_opt,
112 ..
113 },
114 Self::String {
115 val: r_val,
116 optional: r_opt,
117 ..
118 },
119 ) => l_val == r_val && l_opt == r_opt,
120 (
121 Self::Int {
122 val: l_val,
123 optional: l_opt,
124 ..
125 },
126 Self::Int {
127 val: r_val,
128 optional: r_opt,
129 ..
130 },
131 ) => l_val == r_val && l_opt == r_opt,
132 _ => false,
133 }
134 }
135}
136
137impl PartialOrd for PathMember {
138 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
139 match (self, other) {
140 (
141 PathMember::String {
142 val: l_val,
143 optional: l_opt,
144 ..
145 },
146 PathMember::String {
147 val: r_val,
148 optional: r_opt,
149 ..
150 },
151 ) => {
152 let val_ord = Some(l_val.cmp(r_val));
153
154 if let Some(Ordering::Equal) = val_ord {
155 Some(l_opt.cmp(r_opt))
156 } else {
157 val_ord
158 }
159 }
160 (
161 PathMember::Int {
162 val: l_val,
163 optional: l_opt,
164 ..
165 },
166 PathMember::Int {
167 val: r_val,
168 optional: r_opt,
169 ..
170 },
171 ) => {
172 let val_ord = Some(l_val.cmp(r_val));
173
174 if let Some(Ordering::Equal) = val_ord {
175 Some(l_opt.cmp(r_opt))
176 } else {
177 val_ord
178 }
179 }
180 (PathMember::Int { .. }, PathMember::String { .. }) => Some(Ordering::Greater),
181 (PathMember::String { .. }, PathMember::Int { .. }) => Some(Ordering::Less),
182 }
183 }
184}
185
186impl Display for PathMember {
187 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
188 match self {
189 PathMember::Int { val, optional, .. } => {
190 let question_mark = if *optional { "?" } else { "" };
191 write!(f, "{val}{question_mark}")
192 }
193 PathMember::String {
194 val,
195 optional,
196 casing,
197 ..
198 } => {
199 let question_mark = if *optional { "?" } else { "" };
200 let exclamation_mark = if *casing == Casing::Insensitive {
201 "!"
202 } else {
203 ""
204 };
205 let val = if needs_quoting(val) {
206 &escape_quote_string(val)
207 } else {
208 val
209 };
210 write!(f, "{val}{exclamation_mark}{question_mark}")
211 }
212 }
213 }
214}
215
216#[derive(Debug, thiserror::Error)]
217#[error("could not parse path member {attempted:?}")]
218pub struct ParsePathMemberError {
219 attempted: String,
220}
221
222impl FromStr for PathMember {
223 type Err = ParsePathMemberError;
224
225 fn from_str(s: &str) -> Result<Self, Self::Err> {
226 parse::path_member
227 .parse(s)
228 .map_err(|_| ParsePathMemberError {
229 attempted: s.to_owned(),
230 })
231 }
232}
233
234impl Serialize for PathMember {
235 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
236 where
237 S: serde::Serializer,
238 {
239 self.to_string().serialize(serializer)
240 }
241}
242
243impl<'de> Deserialize<'de> for PathMember {
244 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
245 where
246 D: serde::Deserializer<'de>,
247 {
248 let s = String::deserialize(deserializer)?;
249 Self::from_str(&s).map_err(serde::de::Error::custom)
250 }
251}
252
253#[doc(hidden)]
258pub struct TestPathMember<T>(T);
259
260impl<S: Into<String>> From<S> for TestPathMember<String> {
261 fn from(value: S) -> Self {
262 Self(value.into())
263 }
264}
265
266impl TestPathMember<String> {
267 pub fn into_path_member(self) -> PathMember {
268 PathMember::test_string(self.0, false, Casing::Sensitive)
269 }
270}
271
272impl From<usize> for TestPathMember<usize> {
273 fn from(value: usize) -> Self {
274 Self(value)
275 }
276}
277
278impl TestPathMember<usize> {
279 pub fn into_path_member(self) -> PathMember {
280 PathMember::test_int(self.0, false)
281 }
282}
283
284#[derive(Debug, Clone, PartialEq, PartialOrd)]
297pub struct CellPath {
298 pub members: Vec<PathMember>,
299}
300
301impl CellPath {
302 pub fn empty() -> Self {
303 Self {
304 members: Vec::new(),
305 }
306 }
307
308 pub fn make_optional(&mut self) {
309 for member in &mut self.members {
310 member.make_optional();
311 }
312 }
313
314 pub fn make_insensitive(&mut self) {
315 for member in &mut self.members {
316 member.make_insensitive();
317 }
318 }
319
320 pub fn to_column_name(&self) -> String {
322 let mut s = String::new();
323
324 for member in &self.members {
325 match member {
326 PathMember::Int { val, .. } => {
327 s += &val.to_string();
328 }
329 PathMember::String { val, .. } => {
330 s += val;
331 }
332 }
333
334 s.push('.');
335 }
336
337 s.pop(); s
339 }
340
341 pub fn memory_size(&self) -> usize {
343 std::mem::size_of::<Self>() + self.members.iter().map(|m| m.memory_size()).sum::<usize>()
344 }
345
346 pub fn fallback_span(&mut self, span: Span) {
348 for member in self.members.iter_mut() {
349 member.fallback_span(span);
350 }
351 }
352
353 pub fn with_fallback_span(mut self, span: Span) -> Self {
369 self.fallback_span(span);
370 self
371 }
372}
373
374impl Display for CellPath {
375 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
376 write!(f, "$")?;
377 for member in self.members.iter() {
378 write!(f, ".{member}")?;
379 }
380 if self.members.is_empty() {
382 write!(f, ".")?;
383 }
384 Ok(())
385 }
386}
387
388#[derive(Debug, thiserror::Error)]
389#[error("could not parse cell path {attempted:?}")]
390pub struct ParseCellPathError {
391 attempted: String,
392}
393
394impl FromStr for CellPath {
395 type Err = ParseCellPathError;
396
397 fn from_str(s: &str) -> Result<Self, Self::Err> {
398 parse::cell_path.parse(s).map_err(|_| ParseCellPathError {
399 attempted: s.to_owned(),
400 })
401 }
402}
403
404impl Serialize for CellPath {
405 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
406 where
407 S: serde::Serializer,
408 {
409 self.to_string().serialize(serializer)
410 }
411}
412
413impl<'de> Deserialize<'de> for CellPath {
414 fn deserialize<D>(deserializer: D) -> Result<Self, D::Error>
415 where
416 D: serde::Deserializer<'de>,
417 {
418 let s = String::deserialize(deserializer)?;
419 Self::from_str(&s).map_err(serde::de::Error::custom)
420 }
421}
422
423#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
424pub struct FullCellPath {
425 pub head: Expression,
426 pub tail: Vec<PathMember>,
427}
428
429mod parse {
430 use super::*;
431 use winnow::{
432 Result, Str, combinator::*, error::*, prelude::*, stream::ContainsToken, token::*,
433 };
434
435 pub fn cell_path(input: &mut &str) -> Result<CellPath> {
436 preceded(opt("$."), repeat(0.., terminated(path_member, opt('.'))))
437 .parse_next(input)
438 .map(|members| CellPath { members })
439 }
440
441 pub fn path_member(input: &mut &str) -> Result<PathMember> {
442 if input.is_empty() {
443 return Err(ParserError::from_input(input));
444 }
445
446 let member = alt((int_path_member, string_path_member)).parse_next(input)?;
447
448 peek(alt((".", eof))).parse_next(input)?;
450
451 Ok(member)
452 }
453
454 fn int_path_member(input: &mut &str) -> Result<PathMember> {
455 let int = digits.parse_next(input)?;
456 let modifier = modifier.parse_next(input)?;
457 Ok(PathMember::Int {
458 val: int,
459 span: Span::unknown(),
460 optional: modifier.optional,
461 })
462 }
463
464 fn digits(input: &mut &str) -> Result<usize> {
465 let start = input.checkpoint();
466 if let Ok(prefix) = digit_prefix.parse_next(input) {
467 return match prefix {
468 DigitPrefix::Bin => bin_digits.parse_next(input),
469 DigitPrefix::Oct => oct_digits.parse_next(input),
470 DigitPrefix::Hex => hex_digits.parse_next(input),
471 };
472 }
473
474 input.reset(&start);
475 dec_digits.parse_next(input)
476 }
477
478 enum DigitPrefix {
479 Bin,
480 Oct,
481 Hex,
482 }
483
484 fn digit_prefix(input: &mut &str) -> Result<DigitPrefix> {
485 let prefix = take(2usize).parse_next(input)?;
486 Ok(match prefix {
487 "0b" => DigitPrefix::Bin,
488 "0o" => DigitPrefix::Oct,
489 "Ox" => DigitPrefix::Hex,
490 _ => return fail(input),
491 })
492 }
493
494 fn bin_digits(input: &mut &str) -> Result<usize> {
495 any_radix_digits(2, ('_', '0', '1')).parse_next(input)
496 }
497
498 fn oct_digits(input: &mut &str) -> Result<usize> {
499 any_radix_digits(8, ('_', '0'..='7')).parse_next(input)
500 }
501
502 fn dec_digits(input: &mut &str) -> Result<usize> {
503 any_radix_digits(10, ('_', '0'..='9')).parse_next(input)
504 }
505
506 fn hex_digits(input: &mut &str) -> Result<usize> {
507 any_radix_digits(16, ('_', '0'..='9', 'a'..='f', 'A'..='Z')).parse_next(input)
508 }
509
510 fn any_radix_digits<'i>(
511 radix: u32,
512 tokens: impl ContainsToken<char>,
513 ) -> impl Parser<Str<'i>, usize, ContextError> {
514 take_while(1.., tokens)
515 .map(|d: &str| d.replace('_', ""))
516 .verify(|d: &str| !d.is_empty())
517 .try_map(move |d| usize::from_str_radix(&d, radix))
518 }
519
520 fn string_path_member(input: &mut &str) -> Result<PathMember> {
521 let string = alt((
522 single_quoted_string,
523 bare_word_string,
524 double_quoted_string,
525 unquoted_string,
526 ))
527 .parse_next(input)?;
528
529 let modifier = modifier.parse_next(input)?;
530
531 Ok(PathMember::String {
532 val: string,
533 span: Span::unknown(),
534 optional: modifier.optional,
535 casing: match modifier.case_insensitive {
536 true => Casing::Insensitive,
537 false => Default::default(),
538 },
539 })
540 }
541
542 fn unquoted_string(input: &mut &str) -> Result<String> {
543 struct UnquotedTokens;
544
545 impl ContainsToken<char> for UnquotedTokens {
546 fn contains_token(&self, token: char) -> bool {
547 match token {
548 ' ' | '\n' | '\t' => false,
550
551 '!' | '?' | '.' => false,
553
554 '(' | ')' => false,
556
557 _ => true,
558 }
559 }
560 }
561
562 take_while(0.., UnquotedTokens)
563 .parse_next(input)
564 .map(|s| s.to_owned())
565 }
566
567 fn single_quoted_string(input: &mut &str) -> Result<String> {
568 delimited("'", take_while(0.., |c| c != '\''), "'")
569 .parse_next(input)
570 .map(|s| s.to_owned())
571 }
572
573 fn bare_word_string(input: &mut &str) -> Result<String> {
574 delimited("`", take_while(0.., |c| c != '`'), "`")
575 .parse_next(input)
576 .map(|s| s.to_owned())
577 }
578
579 fn double_quoted_string(input: &mut &str) -> Result<String> {
580 fn escaped(input: &mut &str) -> Result<char> {
581 preceded(
582 '\\',
583 alt((
584 'n'.value('\n'),
585 'r'.value('\r'),
586 't'.value('\t'),
587 '\\'.value('\\'),
588 '/'.value('/'),
589 '"'.value('"'),
590 )),
591 )
592 .parse_next(input)
593 }
594
595 fn char(input: &mut &str) -> Result<char> {
596 any.verify(|c| *c != '"').parse_next(input)
597 }
598
599 let content = repeat(0.., alt((escaped, char))).fold(String::new, |mut string, char| {
600 string.push(char);
601 string
602 });
603
604 delimited('"', content, '"').parse_next(input)
605 }
606
607 #[derive(Default)]
608 struct Modifier {
609 optional: bool,
610 case_insensitive: bool,
611 }
612
613 fn modifier(input: &mut &str) -> Result<Modifier> {
614 let mut modifier = Modifier::default();
615
616 loop {
617 let Some(next) = opt(alt(('!', '?'))).parse_next(input)? else {
618 break;
619 };
620
621 let expected = match (next, modifier.optional, modifier.case_insensitive) {
622 ('!', _, false) => {
623 modifier.case_insensitive = true;
624 continue;
625 }
626 ('?', false, _) => {
627 modifier.optional = true;
628 continue;
629 }
630 ('!', false, true) => "'?' or '.'",
631 ('!', true, true) => "'.'",
632 ('?', true, false) => "'!' or '.'",
633 ('?', true, true) => "'.'",
634 (c, _, _) => unreachable!("parser only returns with '!' or '?', got {c:?}"),
635 };
636
637 fail.context(StrContext::Expected(StrContextValue::Description(expected)))
638 .parse_next(input)?
639 }
640
641 Ok(modifier)
642 }
643}
644
645#[cfg(test)]
646mod test {
647 use super::*;
648 use std::cmp::Ordering::Greater;
649
650 #[test]
651 fn path_member_partial_ord() {
652 assert_eq!(
653 Some(Greater),
654 PathMember::test_int(5, true).partial_cmp(&PathMember::test_string(
655 "e",
656 true,
657 Casing::Sensitive
658 ))
659 );
660
661 assert_eq!(
662 Some(Greater),
663 PathMember::test_int(5, true).partial_cmp(&PathMember::test_int(5, false))
664 );
665
666 assert_eq!(
667 Some(Greater),
668 PathMember::test_int(6, true).partial_cmp(&PathMember::test_int(5, true))
669 );
670
671 assert_eq!(
672 Some(Greater),
673 PathMember::test_string("e", true, Casing::Sensitive)
674 .partial_cmp(&PathMember::test_string("e", false, Casing::Sensitive))
675 );
676
677 assert_eq!(
678 Some(Greater),
679 PathMember::test_string("f", true, Casing::Sensitive)
680 .partial_cmp(&PathMember::test_string("e", true, Casing::Sensitive))
681 );
682 }
683}