roan_engine/module/loaders/
mod.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
use crate::{
    context::Context,
    module::{loaders::ident::ModuleIdentifier, Module},
};
use std::{fmt::Debug, path::PathBuf};
use tracing::debug;

pub mod ident;

/// Trait that defines the interface for a module loader.
pub trait ModuleLoader: Debug {
    /// Load a module from a given source.
    fn load(&mut self, referrer: &Module, spec: &str, ctx: &Context) -> anyhow::Result<Module>;

    /// Insert a module into the loader's cache if loader handles caching.
    ///
    /// This function is a no-op for loaders that do not cache modules.
    ///
    /// # Arguments
    /// - `name` - The name of the module to insert into the cache.
    /// - `module` - The module to insert into the cache.
    fn insert(&mut self, name: String, module: Module) {}

    /// Get a module from the cache if the loader caches modules.
    ///
    /// This function returns `None` for loaders that do not cache modules.
    ///
    /// # Arguments
    /// - `name` - The name of the module to get from the cache.
    fn get(&self, name: &str) -> Option<Module> {
        None
    }

    /// Returns all the keys in the cache.
    ///
    /// This function returns an empty vector for loaders that do not cache modules.
    ///
    /// # Returns
    /// A vector of strings representing the keys in the cache.
    fn keys(&self) -> Vec<String> {
        Vec::new()
    }

    /// Resolves the path of a referenced module based on the referrer module's path and the provided specification.
    ///
    /// # Arguments
    ///
    /// * `referrer` - A reference to the `Module` that provides the context for resolving the path.
    /// * `spec` - A string slice that represents the specification of the path to resolve.
    ///
    /// # Returns
    ///
    /// A `Result<PathBuf>`, where the `Ok` variant contains the resolved path, and the `Err` variant
    /// contains an error if the operation fails (e.g., if the `referrer` path has no parent).
    ///
    /// # Panics
    ///
    /// This function will panic if the `referrer` module's path has no parent directory.
    fn resolve_referrer(&self, referrer: &Module, spec: &str) -> anyhow::Result<PathBuf> {
        debug!("Resolving referrer: {:?}, spec: {}", referrer.path(), spec);
        let referrer_path = referrer
            .path()
            .map_or_else(|| PathBuf::new(), |p| p.to_path_buf());
        let dir = referrer_path.parent().expect("Module path has no parent");

        let spec = if cfg!(windows) {
            spec.replace("/", "\\")
        } else {
            spec.to_string()
        };
        let str_path = remove_surrounding_quotes(&spec);

        let spec_path = PathBuf::from(str_path);

        let path = if spec_path.is_absolute() {
            spec_path
        } else {
            dir.join(spec_path)
        };
        debug!("Resolved path: {:?}", path);

        Ok(path)
    }
}

/// Removes surrounding double quotes from a string slice if present.
pub fn remove_surrounding_quotes(s: &str) -> &str {
    if s.starts_with('"') && s.ends_with('"') && s.len() >= 2 {
        &s[1..s.len() - 1]
    } else {
        s
    }
}