rune/
build.rs

1use core::fmt;
2use core::marker::PhantomData;
3use core::mem::take;
4
5use crate::alloc::{self, Vec};
6use crate::ast::{Span, Spanned};
7#[cfg(feature = "std")]
8use crate::compile::FileSourceLoader as DefaultSourceLoader;
9#[cfg(not(feature = "std"))]
10use crate::compile::NoopSourceLoader as DefaultSourceLoader;
11use crate::compile::{
12    self, CompileVisitor, Located, MetaError, Options, ParseOptionError, Pool, SourceLoader,
13};
14use crate::runtime::unit::{DefaultStorage, UnitEncoder};
15use crate::runtime::Unit;
16use crate::sync::Arc;
17use crate::{Context, Diagnostics, Item, SourceId, Sources, Vm};
18
19/// Error raised when we failed to load sources.
20///
21/// Look at the passed in [Diagnostics] instance for details.
22#[derive(Default, Debug)]
23#[non_exhaustive]
24pub struct BuildError {
25    kind: BuildErrorKind,
26}
27
28impl From<ParseOptionError> for BuildError {
29    #[inline]
30    fn from(error: ParseOptionError) -> Self {
31        Self {
32            kind: BuildErrorKind::ParseOptionError(error),
33        }
34    }
35}
36
37impl From<alloc::Error> for BuildError {
38    #[inline]
39    fn from(error: alloc::Error) -> Self {
40        Self {
41            kind: BuildErrorKind::Alloc(error),
42        }
43    }
44}
45
46#[derive(Default, Debug)]
47enum BuildErrorKind {
48    #[default]
49    Default,
50    ParseOptionError(ParseOptionError),
51    Alloc(alloc::Error),
52}
53
54impl fmt::Display for BuildError {
55    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
56        match &self.kind {
57            BuildErrorKind::Default => write!(
58                f,
59                "Failed to build rune sources (see diagnostics for details)"
60            ),
61            BuildErrorKind::ParseOptionError(error) => error.fmt(f),
62            BuildErrorKind::Alloc(error) => error.fmt(f),
63        }
64    }
65}
66
67impl core::error::Error for BuildError {
68    fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
69        match &self.kind {
70            BuildErrorKind::Alloc(error) => Some(error),
71            _ => None,
72        }
73    }
74}
75
76/// Entry point to building a collection [`Sources`] of Rune into a default
77/// executable [`Unit`].
78///
79/// This returns a [`Build`] instance using a default configuration for a build
80/// that can be customized.
81///
82/// By default, if any error is encountered during compilation the error type
83/// [`BuildError`] doesn't provide any diagnostics on what went wrong. To get
84/// rich diagnostics you should instead associated a [`Diagnostics`] type
85/// through [`Build::with_diagnostics`] and examine it before handling any
86/// [`Err`] produced.
87///
88/// Uses the [`Source::name`] when generating diagnostics to reference the file.
89///
90/// [`Source::name`]: crate::Source::name
91///
92/// # Examples
93///
94/// Note: these must be built with the `emit` feature enabled (default) to give
95/// access to `rune::termcolor`.
96///
97/// ```no_run
98/// use rune::termcolor::{ColorChoice, StandardStream};
99/// use rune::{Context, Source, Vm};
100/// use rune::sync::Arc;
101///
102/// let context = Context::with_default_modules()?;
103/// let runtime = Arc::try_new(context.runtime()?)?;
104///
105/// let mut sources = rune::Sources::new();
106///
107/// sources.insert(Source::memory(r#"
108/// pub fn main() {
109///     println!("Hello World");
110/// }
111/// "#)?)?;
112///
113/// let mut diagnostics = rune::Diagnostics::new();
114///
115/// let result = rune::prepare(&mut sources)
116///     .with_context(&context)
117///     .with_diagnostics(&mut diagnostics)
118///     .build();
119///
120/// if !diagnostics.is_empty() {
121///     let mut writer = StandardStream::stderr(ColorChoice::Always);
122///     diagnostics.emit(&mut writer, &sources)?;
123/// }
124///
125/// let unit = result?;
126/// let unit = Arc::try_new(unit)?;
127/// let vm = Vm::new(runtime, unit);
128/// # Ok::<_, rune::support::Error>(())
129/// ```
130pub fn prepare(sources: &mut Sources) -> Build<'_, DefaultStorage> {
131    prepare_with(sources)
132}
133
134/// Prepare with a custom unit storage.
135pub fn prepare_with<S>(sources: &mut Sources) -> Build<'_, S>
136where
137    S: UnitEncoder,
138{
139    Build {
140        sources,
141        context: None,
142        diagnostics: None,
143        options: None,
144        visitors: Vec::new(),
145        source_loader: None,
146        _unit_storage: PhantomData,
147    }
148}
149
150/// A builder for a [Unit].
151///
152/// See [`rune::prepare`] for more.
153///
154/// [`rune::prepare`]: prepare
155pub struct Build<'a, S> {
156    sources: &'a mut Sources,
157    context: Option<&'a Context>,
158    diagnostics: Option<&'a mut Diagnostics>,
159    options: Option<&'a Options>,
160    visitors: Vec<&'a mut dyn compile::CompileVisitor>,
161    source_loader: Option<&'a mut dyn SourceLoader>,
162    _unit_storage: PhantomData<S>,
163}
164
165/// Wraps a collection of CompileVisitor
166struct CompileVisitorGroup<'a> {
167    visitors: Vec<&'a mut dyn compile::CompileVisitor>,
168}
169
170impl compile::CompileVisitor for CompileVisitorGroup<'_> {
171    fn register_meta(&mut self, meta: compile::MetaRef<'_>) -> Result<(), MetaError> {
172        for v in self.visitors.iter_mut() {
173            v.register_meta(meta)?;
174        }
175
176        Ok(())
177    }
178
179    fn visit_meta(
180        &mut self,
181        location: &dyn Located,
182        meta: compile::MetaRef<'_>,
183    ) -> Result<(), MetaError> {
184        for v in self.visitors.iter_mut() {
185            v.visit_meta(location, meta)?;
186        }
187
188        Ok(())
189    }
190
191    fn visit_variable_use(
192        &mut self,
193        source_id: SourceId,
194        var_span: &dyn Spanned,
195        span: &dyn Spanned,
196    ) -> Result<(), MetaError> {
197        for v in self.visitors.iter_mut() {
198            v.visit_variable_use(source_id, var_span, span)?;
199        }
200
201        Ok(())
202    }
203
204    fn visit_mod(&mut self, location: &dyn Located) -> Result<(), MetaError> {
205        for v in self.visitors.iter_mut() {
206            v.visit_mod(location)?;
207        }
208
209        Ok(())
210    }
211
212    fn visit_doc_comment(
213        &mut self,
214        location: &dyn Located,
215        item: &Item,
216        hash: crate::Hash,
217        doc: &str,
218    ) -> Result<(), MetaError> {
219        for v in self.visitors.iter_mut() {
220            v.visit_doc_comment(location, item, hash, doc)?;
221        }
222
223        Ok(())
224    }
225
226    fn visit_field_doc_comment(
227        &mut self,
228        location: &dyn Located,
229        item: &Item,
230        hash: crate::Hash,
231        field: &str,
232        doc: &str,
233    ) -> Result<(), MetaError> {
234        for v in self.visitors.iter_mut() {
235            v.visit_field_doc_comment(location, item, hash, field, doc)?;
236        }
237
238        Ok(())
239    }
240}
241
242impl<'a, S> Build<'a, S> {
243    /// Modify the current [`Build`] to use the given [`Context`] while
244    /// building.
245    ///
246    /// If unspecified the empty context constructed with [`Context::new`] will
247    /// be used. Since this counts as building without a context,
248    /// [`Vm::without_runtime`] can be used when running the produced [`Unit`].
249    #[inline]
250    pub fn with_context(mut self, context: &'a Context) -> Self {
251        self.context = Some(context);
252        self
253    }
254
255    /// Modify the current [Build] to use the given [Diagnostics] collection.
256    #[inline]
257    pub fn with_diagnostics(mut self, diagnostics: &'a mut Diagnostics) -> Self {
258        self.diagnostics = Some(diagnostics);
259        self
260    }
261
262    /// Modify the current [Build] to use the given [Options].
263    #[inline]
264    pub fn with_options(mut self, options: &'a Options) -> Self {
265        self.options = Some(options);
266        self
267    }
268
269    /// Modify the current [Build] to configure the given [CompileVisitor].
270    ///
271    /// A compile visitor allows for custom collecting of compile-time metadata.
272    /// Like if you want to collect every function that is discovered in the
273    /// project.
274    #[inline]
275    pub fn with_visitor(mut self, visitor: &'a mut dyn CompileVisitor) -> alloc::Result<Self> {
276        self.visitors.try_push(visitor)?;
277        Ok(self)
278    }
279
280    /// Modify the current [Build] to configure the given [SourceLoader].
281    ///
282    /// Source loaders are used to determine how sources are loaded externally
283    /// from the current file (as is neede when a module is imported).
284    #[inline]
285    pub fn with_source_loader(mut self, source_loader: &'a mut dyn SourceLoader) -> Self {
286        self.source_loader = Some(source_loader);
287        self
288    }
289
290    /// Build a [`Unit`] with the current configuration.
291    ///
292    /// See [`rune::prepare`] for more.
293    ///
294    /// [`rune::prepare`]: prepare
295    pub fn build(self) -> Result<Unit<S>, BuildError>
296    where
297        S: Default + UnitEncoder,
298    {
299        let (unit, ()) = self.build_inner(|_| ())?;
300        Ok(unit)
301    }
302
303    #[inline]
304    fn build_inner<O>(
305        mut self,
306        extra: impl FnOnce(&Context) -> O,
307    ) -> Result<(Unit<S>, O), BuildError>
308    where
309        S: Default + UnitEncoder,
310    {
311        let default_context;
312
313        let context = match self.context.take() {
314            Some(context) => context,
315            None => {
316                default_context = Context::new();
317                &default_context
318            }
319        };
320
321        let mut unit = compile::UnitBuilder::default();
322
323        let prelude = if context.has_default_modules() {
324            compile::Prelude::with_default_prelude()?
325        } else {
326            compile::Prelude::default()
327        };
328
329        let mut default_diagnostics;
330
331        let diagnostics = match self.diagnostics {
332            Some(diagnostics) => diagnostics,
333            None => {
334                default_diagnostics = Diagnostics::new();
335                &mut default_diagnostics
336            }
337        };
338
339        let default_options;
340
341        let options = match self.options {
342            Some(options) => options,
343            None => {
344                default_options = Options::from_default_env()?;
345                &default_options
346            }
347        };
348
349        let mut default_visitors;
350        let visitors = match self.visitors.is_empty() {
351            true => {
352                default_visitors = CompileVisitorGroup {
353                    visitors: Vec::new(),
354                };
355                &mut default_visitors
356            }
357            false => {
358                let v = take(&mut self.visitors);
359                default_visitors = CompileVisitorGroup { visitors: v };
360
361                &mut default_visitors
362            }
363        };
364
365        let mut default_source_loader;
366
367        let source_loader = match self.source_loader.take() {
368            Some(source_loader) => source_loader,
369            None => {
370                default_source_loader = DefaultSourceLoader::default();
371                &mut default_source_loader
372            }
373        };
374
375        let mut pool = Pool::new()?;
376        let mut unit_storage = S::default();
377
378        compile::compile(
379            &mut unit,
380            &prelude,
381            self.sources,
382            &mut pool,
383            context,
384            visitors,
385            diagnostics,
386            source_loader,
387            options,
388            &mut unit_storage,
389        )?;
390
391        if diagnostics.has_error() {
392            return Err(BuildError::default());
393        }
394
395        if options.link_checks {
396            unit.link(context, diagnostics)?;
397        }
398
399        if diagnostics.has_error() {
400            return Err(BuildError::default());
401        }
402
403        match unit.build(Span::empty(), unit_storage) {
404            Ok(unit) => Ok((unit, extra(context))),
405            Err(error) => {
406                diagnostics.error(SourceId::empty(), error)?;
407                Err(BuildError::default())
408            }
409        }
410    }
411}
412
413impl<'a> Build<'a, DefaultStorage> {
414    /// Convenience method to build a [`Vm`] directly from the current build
415    /// using default storage.
416    pub fn build_vm(self) -> Result<Vm, BuildError> {
417        let (unit, runtime) = self.build_inner(|context| context.runtime())?;
418        let runtime = runtime?;
419        let runtime = Arc::try_new(runtime)?;
420        let unit = Arc::try_new(unit)?;
421        Ok(Vm::new(runtime, unit))
422    }
423}