rune/workspace/
build.rs

1use core::fmt;
2
3use crate::alloc;
4use crate::ast::Span;
5use crate::workspace::manifest::{Loader, Manifest};
6use crate::workspace::{Diagnostics, FileSourceLoader, SourceLoader, WorkspaceError};
7use crate::Sources;
8
9/// Failed to build workspace.
10#[derive(Debug)]
11pub struct BuildError {
12    kind: BuildErrorKind,
13}
14
15impl BuildError {
16    pub(crate) const DEFAULT: Self = Self {
17        kind: BuildErrorKind::Default,
18    };
19}
20
21#[derive(Debug)]
22enum BuildErrorKind {
23    Default,
24    Alloc(alloc::Error),
25}
26
27impl From<alloc::Error> for BuildError {
28    fn from(error: alloc::Error) -> Self {
29        Self {
30            kind: BuildErrorKind::Alloc(error),
31        }
32    }
33}
34
35impl fmt::Display for BuildError {
36    #[inline]
37    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
38        match &self.kind {
39            BuildErrorKind::Default => {
40                write!(f, "Failed to load workspace (see diagnostics for details)")
41            }
42            BuildErrorKind::Alloc(error) => error.fmt(f),
43        }
44    }
45}
46
47impl core::error::Error for BuildError {}
48
49/// Prepare a workspace build.
50pub fn prepare(sources: &mut Sources) -> Build<'_> {
51    Build {
52        sources,
53        diagnostics: None,
54        source_loader: None,
55    }
56}
57
58/// A prepared build.
59pub struct Build<'a> {
60    sources: &'a mut Sources,
61    diagnostics: Option<&'a mut Diagnostics>,
62    source_loader: Option<&'a mut dyn SourceLoader>,
63}
64
65impl<'a> Build<'a> {
66    /// Associate a specific diagnostic with the build.
67    pub fn with_diagnostics(self, diagnostics: &'a mut Diagnostics) -> Self {
68        Self {
69            diagnostics: Some(diagnostics),
70            ..self
71        }
72    }
73
74    /// Associate a specific source loader with the build.
75    ///
76    /// By default [`FileSourceLoader`] will be used.
77    pub fn with_source_loader(self, source_loader: &'a mut dyn SourceLoader) -> Self {
78        Self {
79            source_loader: Some(source_loader),
80            ..self
81        }
82    }
83
84    /// Perform the build.
85    pub fn build(self) -> Result<Manifest, BuildError> {
86        let mut diagnostics;
87
88        let diagnostics = match self.diagnostics {
89            Some(diagnostics) => diagnostics,
90            None => {
91                diagnostics = Diagnostics::new();
92                &mut diagnostics
93            }
94        };
95
96        let mut source_loader;
97
98        let source_loader = match self.source_loader {
99            Some(source_loader) => source_loader,
100            None => {
101                source_loader = FileSourceLoader::new();
102                &mut source_loader
103            }
104        };
105
106        let mut manifest = Manifest::default();
107
108        for id in self.sources.source_ids() {
109            let mut loader =
110                Loader::new(id, self.sources, diagnostics, source_loader, &mut manifest);
111
112            if let Err(error) = loader.load_manifest() {
113                diagnostics.fatal(id, WorkspaceError::new(Span::empty(), error))?;
114            }
115        }
116
117        if diagnostics.has_errors() {
118            return Err(BuildError::DEFAULT);
119        }
120
121        Ok(manifest)
122    }
123}