Skip to main content

nom_grapheme_clusters/
source.rs

1//! Exports source code object and related items.
2
3mod indexing;
4
5#[cfg(test)]
6mod test;
7
8use crate::{location::Location, span::Span};
9pub use indexing::SourceIndex;
10use indexing::{IndexArray, IndexArrayBuilder, IndexArrayIter};
11use std::{
12    cmp::Ordering,
13    fmt,
14    hash::{Hash, Hasher},
15    ops::Index,
16    sync::Arc,
17};
18use unicode_segmentation::UnicodeSegmentation;
19
20#[doc(hidden)]
21pub fn count_grapheme_clusters(input: &str) -> usize {
22    input.graphemes(true).count()
23}
24
25/// Inner structure of a source.
26#[derive(Debug)]
27struct SourceInner {
28    /// File name.
29    name: Box<str>,
30    /// Contents of the source.
31    contents: Box<str>,
32    /// List of string segmentation in the source.
33    segments: IndexArray,
34    /// List of newlines in the source.
35    newlines: IndexArray,
36}
37
38/// A source code object, such as read from a file. Cloning this object results
39/// in simply incrementing a reference counter, thus sharing the source code
40/// object.
41#[derive(Debug, Clone)]
42pub struct Source {
43    /// The inner structure containing the actual data.
44    inner: Arc<SourceInner>,
45}
46
47impl Source {
48    /// Creates a new source code object given its name and its contents.
49    ///
50    /// Contents are rearranged as grapheme clusters.
51    pub fn new<S0, S1>(name: S0, contents: S1) -> Self
52    where
53        S0: Into<Box<str>>,
54        S1: Into<Box<str>>,
55    {
56        let name = name.into();
57        let contents = contents.into();
58        let mut segments = IndexArrayBuilder::new();
59        let mut newlines = IndexArrayBuilder::new();
60
61        for (idx, grapheme) in contents.grapheme_indices(true) {
62            if grapheme == "\n" {
63                newlines.push(segments.len());
64            }
65            segments.push(idx);
66        }
67        segments.push(contents.len());
68
69        let segments = segments.into();
70        let newlines = newlines.into();
71        let inner = SourceInner { name, contents, segments, newlines };
72        Self { inner: Arc::new(inner) }
73    }
74
75    /// The (file) name of the source.
76    pub fn name(&self) -> &str {
77        &self.inner.name
78    }
79
80    /// The length the source.
81    pub fn len(&self) -> usize {
82        self.inner.segments.len() - 1
83    }
84
85    /// The contentss of the source.
86    pub fn contents(&self) -> &str {
87        &self.inner.contents
88    }
89
90    /// Iterator over the segment indices of the source, where indices are in
91    /// terms of bytes.
92    pub fn seg_byte_indices(&self) -> SegmentByteIndices {
93        SegmentByteIndices { inner: self.inner.segments.iter() }
94    }
95
96    /// Iterator over the newline indices of the source, where indices are in
97    /// terms of segments/grapheme clusters.
98    pub fn newline_indices(&self) -> NewlineIndices {
99        NewlineIndices { inner: self.inner.segments.iter() }
100    }
101
102    /// Returns the line number where the given position is contained, starting
103    /// from `0`.
104    pub(super) fn line(&self, position: usize) -> usize {
105        match self.inner.newlines.binary_search(position) {
106            Ok(n) | Err(n) => n,
107        }
108    }
109
110    /// Returns the position of the given line number's start. Line number
111    /// begins at `0`.
112    ///
113    /// # Panics
114    /// Pancis if the given line does not exist.
115    pub(super) fn line_start(&self, line: usize) -> usize {
116        if line == 0 {
117            0
118        } else {
119            self.inner.newlines.index(line - 1) + 1
120        }
121    }
122
123    /// Returns the position of the given line number's start. Line number
124    /// begins at `0`, returning `None` on invalid line number.
125    pub(super) fn try_line_start(&self, line: usize) -> Option<usize> {
126        if line == 0 {
127            Some(0)
128        } else {
129            self.inner.newlines.get(line - 1).map(|position| position + 1)
130        }
131    }
132
133    /// Indexes this source. It can be a single `usize` or a range of `usize`.
134    /// Indices are given in terms of grapheme clusters/segments.
135    pub fn get<I>(&self, indexer: I) -> Option<&I::Output>
136    where
137        I: SourceIndex,
138    {
139        indexer.get(self)
140    }
141
142    /// Returns a span covering the whole source code.
143    pub fn full_span(&self) -> Span {
144        let start = Location::new_unchecked(self.clone(), 0);
145        Span::new_unchecked(start, self.len())
146    }
147}
148
149impl<I> Index<I> for Source
150where
151    I: SourceIndex,
152{
153    type Output = I::Output;
154
155    fn index(&self, indexer: I) -> &Self::Output {
156        indexer.index(self)
157    }
158}
159
160impl PartialEq for Source {
161    fn eq(&self, other: &Self) -> bool {
162        Arc::ptr_eq(&self.inner, &other.inner)
163    }
164}
165
166impl Eq for Source {}
167
168impl PartialOrd for Source {
169    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
170        Some(self.cmp(other))
171    }
172}
173
174impl Ord for Source {
175    fn cmp(&self, other: &Self) -> Ordering {
176        (&*self.inner as *const SourceInner).cmp(&(&*other.inner as *const _))
177    }
178}
179
180impl Hash for Source {
181    fn hash<H>(&self, hasher: &mut H)
182    where
183        H: Hasher,
184    {
185        (&*self.inner as *const SourceInner).hash(hasher)
186    }
187}
188
189impl fmt::Display for Source {
190    fn fmt(&self, fmtr: &mut fmt::Formatter) -> fmt::Result {
191        fmtr.write_str(self.name())
192    }
193}
194
195/// Iterator over the segment indices of a source. Indices are in terms of
196/// bytes. Double-ended and sized.
197#[derive(Debug)]
198pub struct SegmentByteIndices<'src> {
199    /// The inner iterator over the indices.
200    inner: IndexArrayIter<'src>,
201}
202
203impl<'src> Iterator for SegmentByteIndices<'src> {
204    type Item = usize;
205
206    fn next(&mut self) -> Option<Self::Item> {
207        self.inner.next()
208    }
209
210    fn size_hint(&self) -> (usize, Option<usize>) {
211        let len = self.inner.len();
212        (len, Some(len))
213    }
214}
215
216impl<'src> DoubleEndedIterator for SegmentByteIndices<'src> {
217    fn next_back(&mut self) -> Option<Self::Item> {
218        self.inner.next_back()
219    }
220}
221
222impl<'array> ExactSizeIterator for SegmentByteIndices<'array> {}
223
224/// Iterator over the newline indices of a source. Indices are in term of
225/// segments, not bytes nor characters. Double-ended and sized.
226#[derive(Debug)]
227pub struct NewlineIndices<'src> {
228    /// The inner iterator over the indices.
229    inner: IndexArrayIter<'src>,
230}
231
232impl<'src> Iterator for NewlineIndices<'src> {
233    type Item = usize;
234
235    fn next(&mut self) -> Option<Self::Item> {
236        self.inner.next()
237    }
238
239    fn size_hint(&self) -> (usize, Option<usize>) {
240        let len = self.inner.len();
241        (len, Some(len))
242    }
243}
244
245impl<'src> DoubleEndedIterator for NewlineIndices<'src> {
246    fn next_back(&mut self) -> Option<Self::Item> {
247        self.inner.next_back()
248    }
249}
250
251impl<'array> ExactSizeIterator for NewlineIndices<'array> {}