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::{Context, Diagnostics, Item, SourceId, Sources};
17
18#[derive(Default, Debug)]
22#[non_exhaustive]
23pub struct BuildError {
24 kind: BuildErrorKind,
25}
26
27impl From<ParseOptionError> for BuildError {
28 fn from(error: ParseOptionError) -> Self {
29 Self {
30 kind: BuildErrorKind::ParseOptionError(error),
31 }
32 }
33}
34
35impl From<alloc::Error> for BuildError {
36 fn from(error: alloc::Error) -> Self {
37 Self {
38 kind: BuildErrorKind::Alloc(error),
39 }
40 }
41}
42
43#[derive(Default, Debug)]
44enum BuildErrorKind {
45 #[default]
46 Default,
47 ParseOptionError(ParseOptionError),
48 Alloc(alloc::Error),
49}
50
51impl fmt::Display for BuildError {
52 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
53 match &self.kind {
54 BuildErrorKind::Default => write!(
55 f,
56 "Failed to build rune sources (see diagnostics for details)"
57 ),
58 BuildErrorKind::ParseOptionError(error) => error.fmt(f),
59 BuildErrorKind::Alloc(error) => error.fmt(f),
60 }
61 }
62}
63
64impl core::error::Error for BuildError {
65 fn source(&self) -> Option<&(dyn core::error::Error + 'static)> {
66 match &self.kind {
67 BuildErrorKind::Alloc(error) => Some(error),
68 _ => None,
69 }
70 }
71}
72
73pub fn prepare(sources: &mut Sources) -> Build<'_, DefaultStorage> {
128 prepare_with(sources)
129}
130
131pub fn prepare_with<S>(sources: &mut Sources) -> Build<'_, S>
133where
134 S: UnitEncoder,
135{
136 Build {
137 sources,
138 context: None,
139 diagnostics: None,
140 options: None,
141 visitors: Vec::new(),
142 source_loader: None,
143 _unit_storage: PhantomData,
144 }
145}
146
147pub struct Build<'a, S> {
153 sources: &'a mut Sources,
154 context: Option<&'a Context>,
155 diagnostics: Option<&'a mut Diagnostics>,
156 options: Option<&'a Options>,
157 visitors: Vec<&'a mut dyn compile::CompileVisitor>,
158 source_loader: Option<&'a mut dyn SourceLoader>,
159 _unit_storage: PhantomData<S>,
160}
161
162struct CompileVisitorGroup<'a> {
164 visitors: Vec<&'a mut dyn compile::CompileVisitor>,
165}
166
167impl compile::CompileVisitor for CompileVisitorGroup<'_> {
168 fn register_meta(&mut self, meta: compile::MetaRef<'_>) -> Result<(), MetaError> {
169 for v in self.visitors.iter_mut() {
170 v.register_meta(meta)?;
171 }
172
173 Ok(())
174 }
175
176 fn visit_meta(
177 &mut self,
178 location: &dyn Located,
179 meta: compile::MetaRef<'_>,
180 ) -> Result<(), MetaError> {
181 for v in self.visitors.iter_mut() {
182 v.visit_meta(location, meta)?;
183 }
184
185 Ok(())
186 }
187
188 fn visit_variable_use(
189 &mut self,
190 source_id: SourceId,
191 var_span: &dyn Spanned,
192 span: &dyn Spanned,
193 ) -> Result<(), MetaError> {
194 for v in self.visitors.iter_mut() {
195 v.visit_variable_use(source_id, var_span, span)?;
196 }
197
198 Ok(())
199 }
200
201 fn visit_mod(&mut self, location: &dyn Located) -> Result<(), MetaError> {
202 for v in self.visitors.iter_mut() {
203 v.visit_mod(location)?;
204 }
205
206 Ok(())
207 }
208
209 fn visit_doc_comment(
210 &mut self,
211 location: &dyn Located,
212 item: &Item,
213 hash: crate::Hash,
214 doc: &str,
215 ) -> Result<(), MetaError> {
216 for v in self.visitors.iter_mut() {
217 v.visit_doc_comment(location, item, hash, doc)?;
218 }
219
220 Ok(())
221 }
222
223 fn visit_field_doc_comment(
224 &mut self,
225 location: &dyn Located,
226 item: &Item,
227 hash: crate::Hash,
228 field: &str,
229 doc: &str,
230 ) -> Result<(), MetaError> {
231 for v in self.visitors.iter_mut() {
232 v.visit_field_doc_comment(location, item, hash, field, doc)?;
233 }
234
235 Ok(())
236 }
237}
238
239impl<'a, S> Build<'a, S> {
240 #[inline]
249 pub fn with_context(mut self, context: &'a Context) -> Self {
250 self.context = Some(context);
251 self
252 }
253
254 #[inline]
256 pub fn with_diagnostics(mut self, diagnostics: &'a mut Diagnostics) -> Self {
257 self.diagnostics = Some(diagnostics);
258 self
259 }
260
261 #[inline]
263 pub fn with_options(mut self, options: &'a Options) -> Self {
264 self.options = Some(options);
265 self
266 }
267
268 #[inline]
274 pub fn with_visitor(mut self, visitor: &'a mut dyn CompileVisitor) -> alloc::Result<Self> {
275 self.visitors.try_push(visitor)?;
276 Ok(self)
277 }
278
279 #[inline]
284 pub fn with_source_loader(mut self, source_loader: &'a mut dyn SourceLoader) -> Self {
285 self.source_loader = Some(source_loader);
286 self
287 }
288
289 pub fn build(mut self) -> Result<Unit<S>, BuildError>
295 where
296 S: Default + UnitEncoder,
297 {
298 let default_context;
299
300 let context = match self.context.take() {
301 Some(context) => context,
302 None => {
303 default_context = Context::new();
304 &default_context
305 }
306 };
307
308 let mut unit = compile::UnitBuilder::default();
309
310 let prelude = if context.has_default_modules() {
311 compile::Prelude::with_default_prelude()?
312 } else {
313 compile::Prelude::default()
314 };
315
316 let mut default_diagnostics;
317
318 let diagnostics = match self.diagnostics {
319 Some(diagnostics) => diagnostics,
320 None => {
321 default_diagnostics = Diagnostics::new();
322 &mut default_diagnostics
323 }
324 };
325
326 let default_options;
327
328 let options = match self.options {
329 Some(options) => options,
330 None => {
331 default_options = Options::from_default_env()?;
332 &default_options
333 }
334 };
335
336 let mut default_visitors;
337 let visitors = match self.visitors.is_empty() {
338 true => {
339 default_visitors = CompileVisitorGroup {
340 visitors: Vec::new(),
341 };
342 &mut default_visitors
343 }
344 false => {
345 let v = take(&mut self.visitors);
346 default_visitors = CompileVisitorGroup { visitors: v };
347
348 &mut default_visitors
349 }
350 };
351
352 let mut default_source_loader;
353
354 let source_loader = match self.source_loader.take() {
355 Some(source_loader) => source_loader,
356 None => {
357 default_source_loader = DefaultSourceLoader::default();
358 &mut default_source_loader
359 }
360 };
361
362 let mut pool = Pool::new()?;
363 let mut unit_storage = S::default();
364
365 compile::compile(
366 &mut unit,
367 &prelude,
368 self.sources,
369 &mut pool,
370 context,
371 visitors,
372 diagnostics,
373 source_loader,
374 options,
375 &mut unit_storage,
376 )?;
377
378 if diagnostics.has_error() {
379 return Err(BuildError::default());
380 }
381
382 if options.link_checks {
383 unit.link(context, diagnostics)?;
384 }
385
386 if diagnostics.has_error() {
387 return Err(BuildError::default());
388 }
389
390 match unit.build(Span::empty(), unit_storage) {
391 Ok(unit) => Ok(unit),
392 Err(error) => {
393 diagnostics.error(SourceId::empty(), error)?;
394 Err(BuildError::default())
395 }
396 }
397 }
398}