1use core::fmt;
2use core::marker::PhantomData;
3use core::mem::take;
4
5use crate::alloc::borrow::TryToOwned;
6use crate::alloc::{self, String, Vec};
7use crate::ast::{Span, Spanned};
8#[cfg(feature = "std")]
9use crate::compile::FileSourceLoader as DefaultSourceLoader;
10#[cfg(not(feature = "std"))]
11use crate::compile::NoopSourceLoader as DefaultSourceLoader;
12use crate::compile::{
13 self, CompileVisitor, Located, MetaError, Options, ParseOptionError, Pool, SourceLoader,
14};
15use crate::runtime::unit::{DefaultStorage, UnitEncoder};
16use crate::runtime::Unit;
17use crate::sync::Arc;
18use crate::{parse, Context, Diagnostics, Item, SourceId, Sources, Vm};
19
20#[derive(Default, Debug)]
24#[non_exhaustive]
25pub struct BuildError {
26 kind: BuildErrorKind,
27}
28
29impl From<ParseOptionError> for BuildError {
30 #[inline]
31 fn from(error: ParseOptionError) -> Self {
32 Self {
33 kind: BuildErrorKind::ParseOptionError(error),
34 }
35 }
36}
37
38impl From<alloc::Error> for BuildError {
39 #[inline]
40 fn from(error: alloc::Error) -> Self {
41 Self {
42 kind: BuildErrorKind::Alloc(error),
43 }
44 }
45}
46
47#[derive(Default, Debug)]
48enum BuildErrorKind {
49 #[default]
50 Default,
51 ParseOptionError(ParseOptionError),
52 Alloc(alloc::Error),
53}
54
55impl fmt::Display for BuildError {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 match &self.kind {
58 BuildErrorKind::Default => write!(
59 f,
60 "Failed to build rune sources (see diagnostics for details)"
61 ),
62 BuildErrorKind::ParseOptionError(error) => error.fmt(f),
63 BuildErrorKind::Alloc(error) => error.fmt(f),
64 }
65 }
66}
67
68impl core::error::Error for BuildError {
69 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
70 match &self.kind {
71 BuildErrorKind::Alloc(error) => Some(error),
72 _ => None,
73 }
74 }
75}
76
77pub fn prepare(sources: &mut Sources) -> Build<'_, DefaultStorage> {
132 prepare_with(sources)
133}
134
135pub fn prepare_with<S>(sources: &mut Sources) -> Build<'_, S>
137where
138 S: UnitEncoder,
139{
140 Build {
141 sources,
142 context: None,
143 diagnostics: None,
144 options: None,
145 args: Vec::new(),
146 visitors: Vec::new(),
147 source_loader: None,
148 _unit_storage: PhantomData,
149 }
150}
151
152pub struct Build<'a, S> {
158 sources: &'a mut Sources,
159 context: Option<&'a Context>,
160 diagnostics: Option<&'a mut Diagnostics>,
161 options: Option<&'a Options>,
162 args: Vec<String>,
163 visitors: Vec<&'a mut dyn compile::CompileVisitor>,
164 source_loader: Option<&'a mut dyn SourceLoader>,
165 _unit_storage: PhantomData<S>,
166}
167
168struct CompileVisitorGroup<'a> {
170 visitors: Vec<&'a mut dyn compile::CompileVisitor>,
171}
172
173impl compile::CompileVisitor for CompileVisitorGroup<'_> {
174 fn register_meta(&mut self, meta: compile::MetaRef<'_>) -> Result<(), MetaError> {
175 for v in self.visitors.iter_mut() {
176 v.register_meta(meta)?;
177 }
178
179 Ok(())
180 }
181
182 fn visit_meta(
183 &mut self,
184 location: &dyn Located,
185 meta: compile::MetaRef<'_>,
186 ) -> Result<(), MetaError> {
187 for v in self.visitors.iter_mut() {
188 v.visit_meta(location, meta)?;
189 }
190
191 Ok(())
192 }
193
194 fn visit_variable_use(
195 &mut self,
196 source_id: SourceId,
197 var_span: &dyn Spanned,
198 span: &dyn Spanned,
199 ) -> Result<(), MetaError> {
200 for v in self.visitors.iter_mut() {
201 v.visit_variable_use(source_id, var_span, span)?;
202 }
203
204 Ok(())
205 }
206
207 fn visit_mod(&mut self, location: &dyn Located) -> Result<(), MetaError> {
208 for v in self.visitors.iter_mut() {
209 v.visit_mod(location)?;
210 }
211
212 Ok(())
213 }
214
215 fn visit_doc_comment(
216 &mut self,
217 location: &dyn Located,
218 item: &Item,
219 hash: crate::Hash,
220 doc: &str,
221 ) -> Result<(), MetaError> {
222 for v in self.visitors.iter_mut() {
223 v.visit_doc_comment(location, item, hash, doc)?;
224 }
225
226 Ok(())
227 }
228
229 fn visit_field_doc_comment(
230 &mut self,
231 location: &dyn Located,
232 item: &Item,
233 hash: crate::Hash,
234 field: &str,
235 doc: &str,
236 ) -> Result<(), MetaError> {
237 for v in self.visitors.iter_mut() {
238 v.visit_field_doc_comment(location, item, hash, field, doc)?;
239 }
240
241 Ok(())
242 }
243}
244
245impl<'a, S> Build<'a, S> {
246 #[inline]
253 pub fn with_context(mut self, context: &'a Context) -> Self {
254 self.context = Some(context);
255 self
256 }
257
258 #[inline]
260 pub fn with_diagnostics(mut self, diagnostics: &'a mut Diagnostics) -> Self {
261 self.diagnostics = Some(diagnostics);
262 self
263 }
264
265 #[inline]
267 pub fn with_options(mut self, options: &'a Options) -> Self {
268 self.options = Some(options);
269 self
270 }
271
272 pub fn with_arg(mut self, arg: impl AsRef<str>) -> alloc::Result<Self> {
301 self.args.try_push(arg.as_ref().try_to_owned()?)?;
302 Ok(self)
303 }
304
305 pub fn with_args(mut self, args: impl IntoIterator<Item: AsRef<str>>) -> alloc::Result<Self> {
334 for arg in args {
335 self.args.try_push(arg.as_ref().try_to_owned()?)?;
336 }
337
338 Ok(self)
339 }
340
341 #[inline]
347 pub fn with_visitor(mut self, visitor: &'a mut dyn CompileVisitor) -> alloc::Result<Self> {
348 self.visitors.try_push(visitor)?;
349 Ok(self)
350 }
351
352 #[inline]
357 pub fn with_source_loader(mut self, source_loader: &'a mut dyn SourceLoader) -> Self {
358 self.source_loader = Some(source_loader);
359 self
360 }
361
362 pub fn build(self) -> Result<Unit<S>, BuildError>
368 where
369 S: Default + UnitEncoder,
370 {
371 let (unit, ()) = self.build_inner(|_| ())?;
372 Ok(unit)
373 }
374
375 #[inline]
376 fn build_inner<O>(
377 mut self,
378 extra: impl FnOnce(&Context) -> O,
379 ) -> Result<(Unit<S>, O), BuildError>
380 where
381 S: Default + UnitEncoder,
382 {
383 let default_context;
384
385 let context = match self.context.take() {
386 Some(context) => context,
387 None => {
388 default_context = Context::new();
389 &default_context
390 }
391 };
392
393 let mut unit = compile::UnitBuilder::default();
394
395 let prelude = if context.has_default_modules() {
396 compile::Prelude::with_default_prelude()?
397 } else {
398 compile::Prelude::default()
399 };
400
401 let mut default_diagnostics;
402
403 let diagnostics = match self.diagnostics {
404 Some(diagnostics) => diagnostics,
405 None => {
406 default_diagnostics = Diagnostics::new();
407 &mut default_diagnostics
408 }
409 };
410
411 let default_options;
412
413 let options = match self.options {
414 Some(options) => options,
415 None => {
416 default_options = Options::from_default_env()?;
417 &default_options
418 }
419 };
420
421 let mut default_visitors;
422 let visitors = match self.visitors.is_empty() {
423 true => {
424 default_visitors = CompileVisitorGroup {
425 visitors: Vec::new(),
426 };
427 &mut default_visitors
428 }
429 false => {
430 let v = take(&mut self.visitors);
431 default_visitors = CompileVisitorGroup { visitors: v };
432
433 &mut default_visitors
434 }
435 };
436
437 let mut default_source_loader;
438
439 let source_loader = match self.source_loader.take() {
440 Some(source_loader) => source_loader,
441 None => {
442 default_source_loader = DefaultSourceLoader::default();
443 &mut default_source_loader
444 }
445 };
446
447 if !self.args.is_empty() {
448 if let Some(options) = &mut self.options {
449 if !options.script {
450 diagnostics.internal(
451 SourceId::empty(),
452 "cannot set script arguments without enabling script mode",
453 )?;
454
455 return Err(BuildError::default());
456 }
457 }
458
459 for arg in &self.args {
460 if !parse::is_ident(arg) {
461 diagnostics.internal(
462 SourceId::empty(),
463 format!("script argument '{arg}' is not a valid identifier"),
464 )?;
465 }
466 }
467 }
468
469 if diagnostics.has_error() {
470 return Err(BuildError::default());
471 }
472
473 let mut pool = Pool::new()?;
474 let mut unit_storage = S::default();
475
476 compile::compile(
477 &mut unit,
478 &prelude,
479 self.sources,
480 &mut pool,
481 context,
482 visitors,
483 diagnostics,
484 source_loader,
485 options,
486 &self.args,
487 &mut unit_storage,
488 )?;
489
490 if diagnostics.has_error() {
491 return Err(BuildError::default());
492 }
493
494 if options.link_checks {
495 unit.link(context, diagnostics)?;
496 }
497
498 if diagnostics.has_error() {
499 return Err(BuildError::default());
500 }
501
502 match unit.build(Span::empty(), unit_storage) {
503 Ok(unit) => Ok((unit, extra(context))),
504 Err(error) => {
505 diagnostics.error(SourceId::empty(), error)?;
506 Err(BuildError::default())
507 }
508 }
509 }
510}
511
512impl<'a> Build<'a, DefaultStorage> {
513 pub fn build_vm(self) -> Result<Vm, BuildError> {
516 let (unit, runtime) = self.build_inner(|context| context.runtime())?;
517 let runtime = runtime?;
518 let runtime = Arc::try_new(runtime)?;
519 let unit = Arc::try_new(unit)?;
520 Ok(Vm::new(runtime, unit))
521 }
522}