nu_source/
meta.rs

1use crate::pretty::{DbgDocBldr, DebugDocBuilder, PrettyDebugWithSource};
2use crate::text::Text;
3
4use derive_new::new;
5use getset::Getters;
6use serde::Deserialize;
7use serde::Serialize;
8use std::cmp::Ordering;
9use std::path::{Path, PathBuf};
10
11/// Anchors represent a location that a value originated from. The value may have been loaded from a file, fetched from a website, or parsed from some text
12#[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, PartialOrd, Ord, Hash)]
13pub enum AnchorLocation {
14    /// The originating site where the value was first found
15    Url(String),
16    /// The original file where the value was loaded from
17    File(String),
18    /// The text where the value was parsed from
19    Source(Text),
20}
21
22pub trait HasTag {
23    /// Get the associated metadata
24    fn tag(&self) -> Tag;
25}
26
27/// A wrapper type that attaches a Span to a value
28#[derive(new, Debug, Clone, Copy, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
29pub struct Spanned<T> {
30    pub span: Span,
31    pub item: T,
32}
33
34impl<T> Spanned<T> {
35    /// Allows mapping over a Spanned value
36    pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Spanned<U> {
37        let span = self.span;
38
39        let mapped = input(self.item);
40        mapped.spanned(span)
41    }
42}
43
44impl Spanned<String> {
45    /// Iterates over the contained String
46    pub fn items<'a, U>(
47        items: impl Iterator<Item = &'a Spanned<String>>,
48    ) -> impl Iterator<Item = &'a str> {
49        items.map(|item| &item.item[..])
50    }
51
52    /// Borrows the contained String
53    pub fn borrow_spanned(&self) -> Spanned<&str> {
54        let span = self.span;
55        self.item[..].spanned(span)
56    }
57
58    pub fn slice_spanned(&self, span: impl Into<Span>) -> Spanned<&str> {
59        let span = span.into();
60        let item = &self.item[span.start()..span.end()];
61        item.spanned(span)
62    }
63}
64
65pub trait SpannedItem: Sized {
66    /// Converts a value into a Spanned value
67    fn spanned(self, span: impl Into<Span>) -> Spanned<Self> {
68        Spanned {
69            item: self,
70            span: span.into(),
71        }
72    }
73
74    /// Converts a value into a Spanned value, using an unknown Span
75    fn spanned_unknown(self) -> Spanned<Self> {
76        Spanned {
77            item: self,
78            span: Span::unknown(),
79        }
80    }
81}
82impl<T> SpannedItem for T {}
83
84impl<T> std::ops::Deref for Spanned<T> {
85    type Target = T;
86
87    /// Shorthand to deref to the contained value
88    fn deref(&self) -> &T {
89        &self.item
90    }
91}
92
93/// A wrapper type that attaches a Tag to a value
94#[derive(new, Debug, Clone, Eq, PartialEq, Ord, PartialOrd, Serialize, Deserialize, Hash)]
95pub struct Tagged<T> {
96    pub tag: Tag,
97    pub item: T,
98}
99
100impl Tagged<String> {
101    /// Allows borrowing the contained string slice as a spanned value
102    pub fn borrow_spanned(&self) -> Spanned<&str> {
103        let span = self.tag.span;
104        self.item[..].spanned(span)
105    }
106
107    /// Allows borrowing the contained string slice as a tagged value
108    pub fn borrow_tagged(&self) -> Tagged<&str> {
109        self.item[..].tagged(self.tag.clone())
110    }
111}
112
113impl<T> Tagged<Vec<T>> {
114    /// Iterates over the contained value(s)
115    pub fn items(&self) -> impl Iterator<Item = &T> {
116        self.item.iter()
117    }
118}
119
120impl<T> HasTag for Tagged<T> {
121    /// Helper for getting the Tag from the Tagged value
122    fn tag(&self) -> Tag {
123        self.tag.clone()
124    }
125}
126
127impl AsRef<Path> for Tagged<PathBuf> {
128    /// Gets the reference to the contained Path
129    fn as_ref(&self) -> &Path {
130        self.item.as_ref()
131    }
132}
133
134pub trait TaggedItem: Sized {
135    fn tagged(self, tag: impl Into<Tag>) -> Tagged<Self> {
136        Tagged {
137            item: self,
138            tag: tag.into(),
139        }
140    }
141
142    // For now, this is a temporary facility. In many cases, there are other useful spans that we
143    // could be using, such as the original source spans of JSON or Toml files, but we don't yet
144    // have the infrastructure to make that work.
145    fn tagged_unknown(self) -> Tagged<Self> {
146        Tagged {
147            item: self,
148            tag: Tag {
149                span: Span::unknown(),
150                anchor: None,
151            },
152        }
153    }
154}
155
156impl<T> TaggedItem for T {}
157
158impl<T> std::ops::Deref for Tagged<T> {
159    type Target = T;
160
161    fn deref(&self) -> &T {
162        &self.item
163    }
164}
165
166impl<T> Tagged<T> {
167    pub fn map<U>(self, input: impl FnOnce(T) -> U) -> Tagged<U> {
168        let tag = self.tag();
169
170        let mapped = input(self.item);
171        mapped.tagged(tag)
172    }
173
174    pub fn map_anchored(self, anchor: &Option<AnchorLocation>) -> Tagged<T> {
175        let mut tag = self.tag;
176
177        tag.anchor = anchor.clone();
178
179        Tagged {
180            item: self.item,
181            tag,
182        }
183    }
184
185    pub fn transpose(&self) -> Tagged<&T> {
186        Tagged {
187            item: &self.item,
188            tag: self.tag.clone(),
189        }
190    }
191
192    /// Creates a new `Tag` from the current `Tag`
193    pub fn tag(&self) -> Tag {
194        self.tag.clone()
195    }
196
197    /// Retrieve the `Span` for the current `Tag`.
198    pub fn span(&self) -> Span {
199        self.tag.span
200    }
201
202    /// Returns the `AnchorLocation` of the `Tag` if there is one.
203    pub fn anchor(&self) -> Option<AnchorLocation> {
204        self.tag.anchor.clone()
205    }
206
207    /// Returns the underlying `AnchorLocation` variant type as a string.
208    pub fn anchor_name(&self) -> Option<String> {
209        match self.tag.anchor {
210            Some(AnchorLocation::File(ref file)) => Some(file.clone()),
211            Some(AnchorLocation::Url(ref url)) => Some(url.clone()),
212            _ => None,
213        }
214    }
215
216    /// Returns a reference to the current `Tag`'s item.
217    pub fn item(&self) -> &T {
218        &self.item
219    }
220
221    /// Returns a tuple of the `Tagged` item and `Tag`.
222    pub fn into_parts(self) -> (T, Tag) {
223        (self.item, self.tag)
224    }
225}
226
227impl From<&Tag> for Tag {
228    fn from(input: &Tag) -> Tag {
229        input.clone()
230    }
231}
232
233impl From<(usize, usize)> for Span {
234    fn from(input: (usize, usize)) -> Span {
235        Span::new(input.0, input.1)
236    }
237}
238
239impl From<&std::ops::Range<usize>> for Span {
240    fn from(input: &std::ops::Range<usize>) -> Span {
241        Span::new(input.start, input.end)
242    }
243}
244
245/// The set of metadata that can be associated with a value
246#[derive(
247    Debug,
248    Default,
249    Clone,
250    PartialEq,
251    Eq,
252    Ord,
253    PartialOrd,
254    Serialize,
255    Deserialize,
256    Hash,
257    Getters,
258    new,
259)]
260pub struct Tag {
261    /// The original source for this value
262    pub anchor: Option<AnchorLocation>,
263    /// The span in the source text for the command that created this value
264    pub span: Span,
265}
266
267impl From<Span> for Tag {
268    fn from(span: Span) -> Self {
269        Tag { anchor: None, span }
270    }
271}
272
273impl From<&Span> for Tag {
274    fn from(span: &Span) -> Self {
275        Tag {
276            anchor: None,
277            span: *span,
278        }
279    }
280}
281
282impl From<(usize, usize, AnchorLocation)> for Tag {
283    fn from((start, end, anchor): (usize, usize, AnchorLocation)) -> Self {
284        Tag {
285            anchor: Some(anchor),
286            span: Span::new(start, end),
287        }
288    }
289}
290
291impl From<(usize, usize, Option<AnchorLocation>)> for Tag {
292    fn from((start, end, anchor): (usize, usize, Option<AnchorLocation>)) -> Self {
293        Tag {
294            anchor,
295            span: Span::new(start, end),
296        }
297    }
298}
299
300impl From<Tag> for Span {
301    fn from(tag: Tag) -> Self {
302        tag.span
303    }
304}
305
306impl From<&Tag> for Span {
307    fn from(tag: &Tag) -> Self {
308        tag.span
309    }
310}
311
312impl Tag {
313    /// Creates a default `Tag' with unknown `Span` position and no `AnchorLocation`
314    pub fn default() -> Self {
315        Tag {
316            anchor: None,
317            span: Span::unknown(),
318        }
319    }
320
321    pub fn anchored(self, anchor: Option<AnchorLocation>) -> Tag {
322        Tag {
323            anchor,
324            span: self.span,
325        }
326    }
327
328    /// Creates a `Tag` from the given `Span` with no `AnchorLocation`
329    pub fn unknown_anchor(span: Span) -> Tag {
330        Tag { anchor: None, span }
331    }
332
333    /// Creates a `Tag` from the given `AnchorLocation` for a span with a length of 1.
334    pub fn for_char(pos: usize, anchor: AnchorLocation) -> Tag {
335        Tag {
336            anchor: Some(anchor),
337            span: Span::new(pos, pos + 1),
338        }
339    }
340
341    /// Creates a `Tag` for the given `AnchorLocation` with unknown `Span` position.
342    pub fn unknown_span(anchor: AnchorLocation) -> Tag {
343        Tag {
344            anchor: Some(anchor),
345            span: Span::unknown(),
346        }
347    }
348
349    /// Creates a `Tag` with no `AnchorLocation` and an unknown `Span` position.
350    pub fn unknown() -> Tag {
351        Tag {
352            anchor: None,
353            span: Span::unknown(),
354        }
355    }
356
357    /// Returns the `AnchorLocation` of the current `Tag`
358    pub fn anchor(&self) -> Option<AnchorLocation> {
359        self.anchor.clone()
360    }
361
362    // Merges the current `Tag` with the given `Tag`.
363    ///
364    /// Both Tags must share the same `AnchorLocation`.
365    // The resulting `Tag` will have a `Span` that starts from the current `Tag` and ends at `Span` of the given `Tag`.
366    pub fn until(&self, other: impl Into<Tag>) -> Tag {
367        let other = other.into();
368        debug_assert!(
369            self.anchor == other.anchor,
370            "Can only merge two tags with the same anchor"
371        );
372
373        Tag {
374            span: Span::new(self.span.start, other.span.end),
375            anchor: self.anchor.clone(),
376        }
377    }
378
379    /// Merges the current `Tag` with the given optional `Tag`.
380    ///
381    /// Both `Tag`s must share the same `AnchorLocation`.
382    /// The resulting `Tag` will have a `Span` that starts from the current `Tag` and ends at `Span` of the given `Tag`.
383    /// Should the `None` variant be passed in, a new `Tag` with the same `Span` and `AnchorLocation` will be returned.
384    pub fn until_option(&self, other: Option<impl Into<Tag>>) -> Tag {
385        match other {
386            Some(other) => {
387                let other = other.into();
388                debug_assert!(
389                    self.anchor == other.anchor,
390                    "Can only merge two tags with the same anchor"
391                );
392
393                Tag {
394                    span: Span::new(self.span.start, other.span.end),
395                    anchor: self.anchor.clone(),
396                }
397            }
398            None => self.clone(),
399        }
400    }
401
402    pub fn slice<'a>(&self, source: &'a str) -> &'a str {
403        self.span.slice(source)
404    }
405
406    pub fn string(&self, source: &str) -> String {
407        self.span.slice(source).to_string()
408    }
409
410    pub fn tagged_slice<'a>(&self, source: &'a str) -> Tagged<&'a str> {
411        self.span.slice(source).tagged(self)
412    }
413
414    pub fn tagged_string(&self, source: &str) -> Tagged<String> {
415        self.span.slice(source).to_string().tagged(self)
416    }
417
418    pub fn anchor_name(&self) -> Option<String> {
419        match self.anchor {
420            Some(AnchorLocation::File(ref file)) => Some(file.clone()),
421            Some(AnchorLocation::Url(ref url)) => Some(url.clone()),
422            _ => None,
423        }
424    }
425}
426
427pub fn tag_for_tagged_list(mut iter: impl Iterator<Item = Tag>) -> Tag {
428    let first = iter.next();
429
430    let first = match first {
431        None => return Tag::unknown(),
432        Some(first) => first,
433    };
434
435    let last = iter.last();
436
437    match last {
438        None => first,
439        Some(last) => first.until(last),
440    }
441}
442
443pub fn span_for_spanned_list(mut iter: impl Iterator<Item = Span>) -> Span {
444    let first = iter.next();
445
446    let first = match first {
447        None => return Span::unknown(),
448        Some(first) => first,
449    };
450
451    let last = iter.last();
452
453    match last {
454        None => first,
455        Some(last) => first.until(last),
456    }
457}
458
459/// A `Span` is metadata which indicates the start and end positions.
460///
461/// `Span`s are combined with `AnchorLocation`s to form another type of metadata, a `Tag`.
462/// A `Span`'s end position must be greater than or equal to its start position.
463#[derive(
464    Debug, Default, Clone, Copy, PartialEq, Eq, Ord, PartialOrd, Serialize, Deserialize, Hash,
465)]
466pub struct Span {
467    start: usize,
468    end: usize,
469}
470
471impl From<&Span> for Span {
472    fn from(span: &Span) -> Span {
473        *span
474    }
475}
476
477impl From<Option<Span>> for Span {
478    fn from(input: Option<Span>) -> Span {
479        input.unwrap_or_else(|| Span::new(0, 0))
480    }
481}
482
483impl From<Span> for std::ops::Range<usize> {
484    fn from(input: Span) -> std::ops::Range<usize> {
485        std::ops::Range {
486            start: input.start,
487            end: input.end,
488        }
489    }
490}
491
492impl Span {
493    /// Creates a default new `Span` that has 0 start and 0 end.
494    pub fn default() -> Self {
495        Span::unknown()
496    }
497
498    /// Creates a new `Span` that has 0 start and 0 end.
499    pub fn unknown() -> Span {
500        Span::new(0, 0)
501    }
502
503    pub fn from_list(list: &[impl HasSpan]) -> Span {
504        let mut iterator = list.iter();
505
506        match iterator.next() {
507            None => Span::new(0, 0),
508            Some(first) => {
509                let last = iterator.last().unwrap_or(first);
510
511                Span::new(first.span().start, last.span().end)
512            }
513        }
514    }
515
516    /// Creates a new `Span` from start and end inputs. The end parameter must be greater than or equal to the start parameter.
517    pub fn new(start: usize, end: usize) -> Span {
518        assert!(
519            end >= start,
520            "Can't create a Span whose end < start, start={}, end={}",
521            start,
522            end
523        );
524
525        Span { start, end }
526    }
527
528    pub fn new_option(start: usize, end: usize) -> Option<Span> {
529        if end >= start {
530            None
531        } else {
532            Some(Span { start, end })
533        }
534    }
535
536    /// Creates a `Span` with a length of 1 from the given position.
537    ///
538    /// # Example
539    ///
540    /// ```
541    /// let char_span = Span::for_char(5);
542    ///
543    /// assert_eq!(char_span.start(), 5);
544    /// assert_eq!(char_span.end(), 6);
545    /// ```
546    pub fn for_char(pos: usize) -> Span {
547        Span {
548            start: pos,
549            end: pos + 1,
550        }
551    }
552
553    /// Returns a bool indicating if the given position falls inside the current `Span`.
554    ///
555    /// # Example
556    ///
557    /// ```
558    /// let span = Span::new(2, 8);
559    ///
560    /// assert_eq!(span.contains(5), true);
561    /// assert_eq!(span.contains(8), false);
562    /// assert_eq!(span.contains(100), false);
563    /// ```
564    pub fn contains(&self, pos: usize) -> bool {
565        self.start <= pos && pos < self.end
566    }
567
568    /// Returns a new Span by merging an earlier Span with the current Span.
569    ///
570    /// The resulting Span will have the same start position as the given Span and same end as the current Span.
571    ///
572    /// # Example
573    ///
574    /// ```
575    /// let original_span = Span::new(4, 6);
576    /// let earlier_span = Span::new(1, 3);
577    /// let merged_span = origin_span.since(earlier_span);
578    ///
579    /// assert_eq!(merged_span.start(), 1);
580    /// assert_eq!(merged_span.end(), 6);
581    /// ```
582    pub fn since(&self, other: impl Into<Span>) -> Span {
583        let other = other.into();
584
585        Span::new(other.start, self.end)
586    }
587
588    /// Returns a new Span by merging a later Span with the current Span.
589    ///
590    /// The resulting Span will have the same start position as the current Span and same end as the given Span.
591    ///
592    /// # Example
593    ///
594    /// ```
595    /// let original_span = Span::new(4, 6);
596    /// let later_span = Span::new(9, 11);
597    /// let merged_span = origin_span.until(later_span);
598    ///
599    /// assert_eq!(merged_span.start(), 4);
600    /// assert_eq!(merged_span.end(), 11);
601    /// ```
602    pub fn until(&self, other: impl Into<Span>) -> Span {
603        let other = other.into();
604
605        Span::new(self.start, other.end)
606    }
607
608    pub fn merge(&self, other: impl Into<Span>) -> Span {
609        let other = other.into();
610
611        if other.end < self.start {
612            other.until(self)
613        } else {
614            self.until(other)
615        }
616    }
617
618    /// Returns a new Span by merging a later Span with the current Span.
619    ///
620    /// If the given Span is of the None variant,
621    /// A Span with the same values as the current Span is returned.
622    pub fn until_option(&self, other: Option<impl Into<Span>>) -> Span {
623        match other {
624            Some(other) => {
625                let other = other.into();
626
627                Span::new(self.start, other.end)
628            }
629            None => *self,
630        }
631    }
632
633    pub fn string(&self, source: &str) -> String {
634        self.slice(source).to_string()
635    }
636
637    pub fn spanned_slice<'a>(&self, source: &'a str) -> Spanned<&'a str> {
638        self.slice(source).spanned(*self)
639    }
640
641    pub fn spanned_string(&self, source: &str) -> Spanned<String> {
642        self.slice(source).to_string().spanned(*self)
643    }
644
645    /// Returns the start position of the current Span.
646    pub fn start(&self) -> usize {
647        self.start
648    }
649
650    /// Returns the end position of the current Span.
651    pub fn end(&self) -> usize {
652        self.end
653    }
654
655    /// Returns a bool if the current Span indicates an "unknown"  position.
656    ///
657    /// # Example
658    ///
659    /// ```
660    /// let unknown_span = Span::unknown();
661    /// let known_span = Span::new(4, 6);
662    ///
663    /// assert_eq!(unknown_span.is_unknown(), true);
664    /// assert_eq!(known_span.is_unknown(), false);
665    /// ```
666    pub fn is_unknown(&self) -> bool {
667        self.start == 0 && self.end == 0
668    }
669
670    /// Returns a bool if the current Span does not cover.
671    ///
672    /// # Example
673    ///
674    /// ```
675    /// //  make clean
676    /// //  ----
677    /// //  (0,4)
678    /// //
679    /// //       ^(5,5)
680    ///
681    /// let make_span = Span::new(0,4);
682    /// let clean_span = Span::new(5,5);
683    ///
684    /// assert_eq!(make_span.is_closed(), false);
685    /// assert_eq!(clean_span.is_closed(), true);
686    /// ```
687    pub fn is_closed(&self) -> bool {
688        self.start == self.end
689    }
690
691    /// Returns a slice of the input that covers the start and end of the current Span.
692    pub fn slice<'a>(&self, source: &'a str) -> &'a str {
693        &source[self.start..self.end]
694    }
695}
696
697impl PartialOrd<usize> for Span {
698    fn partial_cmp(&self, other: &usize) -> Option<Ordering> {
699        (self.end - self.start).partial_cmp(other)
700    }
701}
702
703impl PartialEq<usize> for Span {
704    fn eq(&self, other: &usize) -> bool {
705        (self.end - self.start) == *other
706    }
707}
708
709pub trait IntoSpanned {
710    type Output: HasFallibleSpan;
711
712    fn into_spanned(self, span: impl Into<Span>) -> Self::Output;
713}
714
715impl<T: HasFallibleSpan> IntoSpanned for T {
716    type Output = T;
717    fn into_spanned(self, _span: impl Into<Span>) -> Self::Output {
718        self
719    }
720}
721
722pub trait HasSpan {
723    fn span(&self) -> Span;
724}
725
726impl<T, E> HasSpan for Result<T, E>
727where
728    T: HasSpan,
729{
730    fn span(&self) -> Span {
731        match self {
732            Result::Ok(val) => val.span(),
733            Result::Err(_) => Span::unknown(),
734        }
735    }
736}
737
738impl<T> HasSpan for Spanned<T> {
739    fn span(&self) -> Span {
740        self.span
741    }
742}
743
744pub trait HasFallibleSpan {
745    fn maybe_span(&self) -> Option<Span>;
746}
747
748impl HasFallibleSpan for bool {
749    fn maybe_span(&self) -> Option<Span> {
750        None
751    }
752}
753
754impl HasFallibleSpan for () {
755    fn maybe_span(&self) -> Option<Span> {
756        None
757    }
758}
759
760impl<T> HasFallibleSpan for T
761where
762    T: HasSpan,
763{
764    fn maybe_span(&self) -> Option<Span> {
765        Some(HasSpan::span(self))
766    }
767}
768
769impl PrettyDebugWithSource for Option<Span> {
770    fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
771        match self {
772            None => DbgDocBldr::description("no span"),
773            Some(span) => span.pretty_debug(source),
774        }
775    }
776}
777
778impl HasFallibleSpan for Option<Span> {
779    fn maybe_span(&self) -> Option<Span> {
780        *self
781    }
782}
783
784impl PrettyDebugWithSource for Span {
785    fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
786        DbgDocBldr::typed(
787            "span",
788            DbgDocBldr::keyword("for")
789                + DbgDocBldr::space()
790                + DbgDocBldr::description(format!("{:?}", self.slice(source))),
791        )
792    }
793}
794
795impl HasSpan for Span {
796    fn span(&self) -> Span {
797        *self
798    }
799}
800
801impl<T> PrettyDebugWithSource for Option<Spanned<T>>
802where
803    Spanned<T>: PrettyDebugWithSource,
804{
805    fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
806        match self {
807            None => DbgDocBldr::description("nothing"),
808            Some(v) => v.pretty_debug(v.span.slice(source)),
809        }
810    }
811}
812
813impl<T> HasFallibleSpan for Option<Spanned<T>> {
814    fn maybe_span(&self) -> Option<Span> {
815        self.as_ref().map(|value| value.span)
816    }
817}
818
819impl<T> PrettyDebugWithSource for Option<Tagged<T>>
820where
821    Tagged<T>: PrettyDebugWithSource,
822{
823    fn pretty_debug(&self, source: &str) -> DebugDocBuilder {
824        match self {
825            None => DbgDocBldr::description("nothing"),
826            Some(d) => d.pretty_debug(source),
827        }
828    }
829}
830
831impl<T> HasFallibleSpan for Option<Tagged<T>> {
832    fn maybe_span(&self) -> Option<Span> {
833        self.as_ref().map(|value| value.tag.span)
834    }
835}
836
837impl<T> HasSpan for Tagged<T> {
838    fn span(&self) -> Span {
839        self.tag.span
840    }
841}