rune/compile/
source_loader.rs

1use crate::alloc::path::Path;
2#[cfg(feature = "std")]
3use crate::alloc::prelude::*;
4use crate::ast::Spanned;
5use crate::compile;
6#[cfg(feature = "std")]
7use crate::compile::ErrorKind;
8#[cfg(feature = "std")]
9use crate::item::ComponentRef;
10use crate::{Item, Source};
11
12/// A source loader.
13pub trait SourceLoader {
14    /// Load the given URL.
15    fn load(&mut self, root: &Path, item: &Item, span: &dyn Spanned) -> compile::Result<Source>;
16}
17
18/// A source loader which does not support loading anything and will error.
19#[derive(Default)]
20#[non_exhaustive]
21pub struct NoopSourceLoader;
22
23impl SourceLoader for NoopSourceLoader {
24    fn load(&mut self, _: &Path, _: &Item, span: &dyn Spanned) -> compile::Result<Source> {
25        Err(compile::Error::msg(span, "Source loading is not supported"))
26    }
27}
28
29cfg_std! {
30    /// A filesystem-based source loader.
31    #[derive(Default)]
32    #[non_exhaustive]
33    pub struct FileSourceLoader;
34
35    impl FileSourceLoader {
36        /// Construct a new filesystem-based source loader.
37        pub fn new() -> Self {
38            Self::default()
39        }
40    }
41
42    impl SourceLoader for FileSourceLoader {
43        fn load(&mut self, root: &Path, item: &Item, span: &dyn Spanned) -> compile::Result<Source> {
44            let mut base = root.try_to_owned()?;
45
46            if !base.pop() {
47                return Err(compile::Error::new(
48                    span,
49                    ErrorKind::UnsupportedModuleRoot {
50                        root: root.try_to_owned()?,
51                    },
52                ));
53            }
54
55            for c in item {
56                if let ComponentRef::Str(string) = c {
57                    base.push(string);
58                } else {
59                    return Err(compile::Error::new(
60                        span,
61                        ErrorKind::UnsupportedModuleItem {
62                            item: item.try_to_owned()?,
63                        },
64                    ));
65                }
66            }
67
68            let candidates = [base.join("mod.rn"), base.with_extension("rn")];
69
70            let mut found = None;
71
72            for path in &candidates[..] {
73                if path.is_file() {
74                    found = Some(path);
75                    break;
76                }
77            }
78
79            let Some(path) = found else {
80                return Err(compile::Error::new(
81                    span,
82                    ErrorKind::ModNotFound { path: base },
83                ));
84            };
85
86            match Source::from_path(path) {
87                Ok(source) => Ok(source),
88                Err(error) => Err(compile::Error::new(
89                    span,
90                    ErrorKind::SourceError {
91                        path: path.clone(),
92                        error,
93                    },
94                )),
95            }
96        }
97    }
98}