rune/compile/
source_loader.rs1#[cfg(feature = "std")]
2use crate::alloc::prelude::*;
3use crate::ast::Spanned;
4use crate::compile;
5#[cfg(feature = "std")]
6use crate::compile::ErrorKind;
7#[cfg(feature = "std")]
8use crate::item::ComponentRef;
9use crate::{Item, Source, SourceId, Sources};
10
11pub trait SourceLoader {
13 fn load(
15 &mut self,
16 sources: &Sources,
17 id: SourceId,
18 item: &Item,
19 span: &dyn Spanned,
20 ) -> compile::Result<Source>;
21}
22
23#[derive(Default)]
25#[non_exhaustive]
26pub struct NoopSourceLoader;
27
28impl SourceLoader for NoopSourceLoader {
29 fn load(
30 &mut self,
31 _: &Sources,
32 _: SourceId,
33 _: &Item,
34 span: &dyn Spanned,
35 ) -> compile::Result<Source> {
36 Err(compile::Error::msg(span, "Source loading is not supported"))
37 }
38}
39
40#[derive(Default)]
42#[non_exhaustive]
43#[cfg(feature = "std")]
44#[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))]
45pub struct FileSourceLoader;
46
47#[cfg(feature = "std")]
48#[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))]
49impl FileSourceLoader {
50 pub fn new() -> Self {
52 Self::default()
53 }
54}
55
56#[cfg(feature = "std")]
57#[cfg_attr(rune_docsrs, doc(cfg(feature = "std")))]
58impl SourceLoader for FileSourceLoader {
59 fn load(
60 &mut self,
61 sources: &Sources,
62 id: SourceId,
63 item: &Item,
64 span: &dyn Spanned,
65 ) -> compile::Result<Source> {
66 let Some(base) = sources.path(id) else {
67 return Err(compile::Error::new(span, ErrorKind::SourceWithoutPath));
68 };
69
70 let mut base = base.try_to_owned()?;
71
72 if !base.pop() {
73 return Err(compile::Error::new(
74 span,
75 ErrorKind::UnsupportedModuleRoot {
76 root: base.try_to_owned()?,
77 },
78 ));
79 }
80
81 for c in item {
82 if let ComponentRef::Str(string) = c {
83 base.push(string);
84 } else {
85 return Err(compile::Error::new(
86 span,
87 ErrorKind::UnsupportedModuleItem {
88 item: item.try_to_owned()?,
89 },
90 ));
91 }
92 }
93
94 let candidates = [base.join("mod.rn"), base.with_extension("rn")];
95
96 let mut found = None;
97
98 for path in &candidates[..] {
99 if path.is_file() {
100 found = Some(path);
101 break;
102 }
103 }
104
105 let Some(path) = found else {
106 return Err(compile::Error::new(
107 span,
108 ErrorKind::ModNotFound { path: base },
109 ));
110 };
111
112 match Source::from_path(path) {
113 Ok(source) => Ok(source),
114 Err(error) => Err(compile::Error::new(
115 span,
116 ErrorKind::SourceError {
117 path: path.clone(),
118 error,
119 },
120 )),
121 }
122 }
123}