rune/
sources.rs

1use core::fmt;
2use core::num;
3
4#[cfg(feature = "musli")]
5use musli::{Decode, Encode};
6#[cfg(feature = "serde")]
7use serde::{Deserialize, Serialize};
8
9use crate as rune;
10use crate::alloc;
11use crate::alloc::path::Path;
12use crate::alloc::prelude::*;
13use crate::ast::Span;
14use crate::source::Source;
15#[cfg(feature = "codespan-reporting")]
16use codespan_reporting::files;
17
18/// Helper macro to define a collection of sources populatedc with the given
19/// entries.
20///
21/// Calling this macro is fallible with [alloc::Error], so you should do it in a
22/// function that returns a `Result`.
23///
24/// ```
25/// let sources = rune::sources! {
26///     entry => {
27///         pub fn main() {
28///             42
29///         }
30///     }
31/// };
32/// # Ok::<_, rune::support::Error>(())
33/// ```
34#[macro_export]
35macro_rules! sources {
36    ($($name:ident => {$($tt:tt)*}),* $(,)?) => {{
37        let mut sources = $crate::Sources::new();
38        $(sources.insert($crate::Source::new(stringify!($name), stringify!($($tt)*))?)?;)*
39        sources
40    }};
41}
42
43/// A collection of source files.
44#[derive(Debug, Default)]
45pub struct Sources {
46    /// Sources associated.
47    sources: Vec<Source>,
48}
49
50impl Sources {
51    /// Construct a new collection of sources.
52    #[inline]
53    pub fn new() -> Self {
54        Self {
55            sources: Vec::new(),
56        }
57    }
58
59    /// Insert a source and return its [`SourceId`].
60    ///
61    /// # Examples
62    ///
63    /// ```
64    /// use rune::{Sources, Source};
65    ///
66    /// let mut sources = Sources::new();
67    /// let id = sources.insert(Source::new("<memory>", "pub fn main() { 10 }")?)?;
68    /// let id2 = sources.insert(Source::new("<memory>", "pub fn main() { 10 }")?)?;
69    /// assert_ne!(id, id2);
70    /// # Ok::<_, rune::support::Error>(())
71    /// ```
72    #[inline]
73    pub fn insert(&mut self, source: Source) -> alloc::Result<SourceId> {
74        let id =
75            SourceId::try_from(self.sources.len()).expect("could not build a source identifier");
76        self.sources.try_push(source)?;
77        Ok(id)
78    }
79
80    /// Get the source matching the given source id.
81    ///
82    /// # Examples
83    ///
84    /// ```
85    /// # use anyhow::Context;
86    /// use rune::{Sources, Source};
87    ///
88    /// let mut sources = Sources::new();
89    /// let id = sources.insert(Source::new("<memory>", "pub fn main() { 10 }")?)?;
90    ///
91    /// let source = sources.get(id).context("expected source")?;
92    ///
93    /// assert_eq!(source.name(), "<memory>");
94    /// # Ok::<_, rune::support::Error>(())
95    /// ```
96    #[inline]
97    pub fn get(&self, id: SourceId) -> Option<&Source> {
98        self.sources.get(id.into_index())
99    }
100
101    /// Fetch name for the given source id.
102    #[inline]
103    pub(crate) fn name(&self, id: SourceId) -> Option<&str> {
104        let source = self.sources.get(id.into_index())?;
105        Some(source.name())
106    }
107
108    /// Fetch source for the given span.
109    #[inline]
110    pub(crate) fn source(&self, id: SourceId, span: Span) -> Option<&str> {
111        let source = self.sources.get(id.into_index())?;
112        source.get(span.range())
113    }
114
115    /// Access the optional path of the given source id.
116    #[inline]
117    pub(crate) fn path(&self, id: SourceId) -> Option<&Path> {
118        let source = self.sources.get(id.into_index())?;
119        source.path()
120    }
121
122    /// Get all available source ids.
123    #[inline]
124    pub(crate) fn source_ids(&self) -> impl Iterator<Item = SourceId> {
125        (0..self.sources.len()).map(|index| SourceId::new(index as u32))
126    }
127
128    /// Iterate over all registered sources.
129    #[cfg(feature = "cli")]
130    #[inline]
131    pub(crate) fn iter(&self) -> impl Iterator<Item = &Source> {
132        self.sources.iter()
133    }
134}
135
136#[cfg(feature = "codespan-reporting")]
137impl<'a> files::Files<'a> for Sources {
138    type FileId = SourceId;
139    type Name = &'a str;
140    type Source = &'a str;
141
142    #[inline]
143    fn name(&'a self, file_id: SourceId) -> Result<Self::Name, files::Error> {
144        let source = self.get(file_id).ok_or(files::Error::FileMissing)?;
145        Ok(source.name())
146    }
147
148    #[inline]
149    fn source(&'a self, file_id: SourceId) -> Result<Self::Source, files::Error> {
150        let source = self.get(file_id).ok_or(files::Error::FileMissing)?;
151        Ok(source.as_str())
152    }
153
154    #[cfg(feature = "emit")]
155    #[inline]
156    fn line_index(&self, file_id: SourceId, byte_index: usize) -> Result<usize, files::Error> {
157        let source = self.get(file_id).ok_or(files::Error::FileMissing)?;
158        Ok(source.line_index(byte_index))
159    }
160
161    #[cfg(feature = "emit")]
162    #[inline]
163    fn line_range(
164        &self,
165        file_id: SourceId,
166        line_index: usize,
167    ) -> Result<std::ops::Range<usize>, files::Error> {
168        let source = self.get(file_id).ok_or(files::Error::FileMissing)?;
169        let range = source
170            .line_range(line_index)
171            .ok_or_else(|| files::Error::LineTooLarge {
172                given: line_index,
173                max: source.line_count(),
174            })?;
175        Ok(range)
176    }
177}
178
179/// The opaque identifier of a source file, as returned by
180/// [`Sources::insert`].
181///
182/// It can be used to reference the inserted source file in the future through
183/// methods such as [`Sources::get`].
184#[derive(TryClone, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
185#[try_clone(copy)]
186#[repr(transparent)]
187#[cfg_attr(feature = "serde", derive(Serialize, Deserialize), serde(transparent))]
188#[cfg_attr(feature = "musli", derive(Encode, Decode), musli(transparent))]
189pub struct SourceId {
190    index: u32,
191}
192
193impl SourceId {
194    /// The empty source identifier.
195    pub const EMPTY: Self = Self::empty();
196
197    /// Construct a source identifier from an index.
198    #[inline]
199    pub const fn new(index: u32) -> Self {
200        Self { index }
201    }
202
203    /// Define an empty source identifier that cannot reference a source.
204    #[inline]
205    pub const fn empty() -> Self {
206        Self { index: u32::MAX }
207    }
208
209    /// Access the source identifier as an index.
210    #[inline]
211    pub fn into_index(self) -> usize {
212        usize::try_from(self.index).expect("source id out of bounds")
213    }
214}
215
216impl fmt::Debug for SourceId {
217    #[inline]
218    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
219        self.index.fmt(f)
220    }
221}
222
223impl fmt::Display for SourceId {
224    #[inline]
225    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
226        self.index.fmt(f)
227    }
228}
229
230impl Default for SourceId {
231    #[inline]
232    fn default() -> Self {
233        Self::empty()
234    }
235}
236
237impl TryFrom<usize> for SourceId {
238    type Error = num::TryFromIntError;
239
240    #[inline]
241    fn try_from(value: usize) -> Result<Self, Self::Error> {
242        Ok(Self {
243            index: u32::try_from(value)?,
244        })
245    }
246}