rune/compile/
source_loader.rs

1#[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
11/// A source loader.
12pub trait SourceLoader {
13    /// Load the given URL.
14    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/// A source loader which does not support loading anything and will error.
24#[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/// A filesystem-based source loader.
41#[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    /// Construct a new filesystem-based source loader.
51    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}