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#[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
76pub fn prepare(sources: &mut Sources) -> Build<'_, DefaultStorage> {
131 prepare_with(sources)
132}
133
134pub 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
150pub 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
165struct 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 #[inline]
250 pub fn with_context(mut self, context: &'a Context) -> Self {
251 self.context = Some(context);
252 self
253 }
254
255 #[inline]
257 pub fn with_diagnostics(mut self, diagnostics: &'a mut Diagnostics) -> Self {
258 self.diagnostics = Some(diagnostics);
259 self
260 }
261
262 #[inline]
264 pub fn with_options(mut self, options: &'a Options) -> Self {
265 self.options = Some(options);
266 self
267 }
268
269 #[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 #[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 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 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}