libmoonwave/
span.rs

1use std::{fmt, ops::Deref};
2
3use crate::{diagnostic::Diagnostic, doc_comment::DocComment};
4
5#[derive(Debug, Copy, Clone, Default)]
6pub struct Span<'a> {
7    source: &'a str,
8    pub start: usize,
9    pub len: usize,
10    pub file_id: usize,
11    pub source_offset: usize,
12}
13
14impl Span<'static> {
15    pub fn empty(file_id: usize) -> Self {
16        Span {
17            source: "",
18            file_id,
19            ..Default::default()
20        }
21    }
22}
23
24impl<'a> Span<'a> {
25    pub fn dummy(source: &'a str) -> Self {
26        Self {
27            source,
28            len: source.len(),
29            ..Default::default()
30        }
31    }
32
33    pub fn slice(&self, start: usize, len: usize) -> Self {
34        Self {
35            start: self.start + start,
36            len,
37            ..*self
38        }
39    }
40
41    pub fn as_str(&self) -> &'a str {
42        &self.source[self.start..self.start + self.len]
43    }
44
45    pub fn lines(self) -> impl Iterator<Item = Span<'a>> {
46        self.as_str().lines().map(move |line| self.from_slice(line))
47    }
48
49    pub fn splitn(self, n: usize, pat: &'static str) -> impl Iterator<Item = Span<'a>> {
50        self.as_str()
51            .splitn(n, pat)
52            .map(move |piece: &'a str| self.from_slice(piece))
53    }
54
55    pub fn trim(self) -> Self {
56        self.from_slice(self.as_str().trim())
57    }
58
59    pub fn strip_prefix(self, prefix: &str) -> Option<Self> {
60        Some(self.from_slice(self.as_str().strip_prefix(prefix)?))
61    }
62
63    pub fn diagnostic<S: Into<String>>(self, text: S) -> Diagnostic {
64        Diagnostic::from_span(text, self)
65    }
66
67    pub fn replace(&mut self, span: Self) {
68        *self = span // Is this legal?
69    }
70
71    fn from_slice(&self, text: &'a str) -> Self {
72        let start = text.as_ptr() as usize - self.source.as_ptr() as usize;
73
74        Span {
75            start,
76            len: text.len(),
77            ..*self
78        }
79    }
80}
81
82impl PartialEq for Span<'_> {
83    fn eq(&self, other: &Self) -> bool {
84        self.as_str() == other.as_str()
85    }
86}
87
88impl fmt::Display for Span<'_> {
89    fn fmt(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
90        formatter.write_str(self.as_str())
91    }
92}
93
94impl serde::Serialize for Span<'_> {
95    fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
96        serializer.serialize_str(self.as_str())
97    }
98}
99
100impl Deref for Span<'_> {
101    type Target = str;
102    fn deref(&self) -> &str {
103        self.as_str()
104    }
105}
106
107impl<'a> From<&'a DocComment> for Span<'a> {
108    fn from(doc: &'a DocComment) -> Self {
109        Span {
110            source: &doc.comment,
111            len: doc.comment.len(),
112            file_id: doc.file_id,
113            source_offset: doc.start,
114            ..Default::default()
115        }
116    }
117}
118
119#[cfg(test)]
120mod test {
121    use super::*;
122
123    #[test]
124    fn correct_deref() {
125        let text = "abcdef";
126        let span = Span::dummy(text);
127
128        let deref_check: &str = &span;
129        assert_eq!(deref_check, text);
130
131        let slice = span.slice(1, 3);
132        let deref_slice_check: &str = &slice;
133        assert_eq!(deref_slice_check, "bcd");
134
135        let slice = slice.slice(1, 2);
136        let deref_slice_check: &str = &slice;
137        assert_eq!(deref_slice_check, "cd");
138    }
139
140    #[test]
141    fn lines() {
142        let text = "hello\nworld!\nipsum";
143        let span = Span::dummy(text);
144
145        let lines: Vec<_> = span.lines().map(|line| line.as_str()).collect();
146        assert_eq!(lines, &["hello", "world!", "ipsum"]);
147    }
148
149    #[test]
150    fn trim() {
151        let text = "    hello       ";
152        let span = Span::dummy(text);
153
154        assert_eq!(span.trim().as_str(), "hello");
155    }
156}