shape_runtime/module_loader/
resolver.rs1use shape_ast::error::{Result, ShapeError};
9use std::collections::HashMap;
10use std::path::{Path, PathBuf};
11use std::sync::Arc;
12
13#[derive(Debug, Clone, PartialEq, Eq)]
15pub enum ModuleCode {
16 Source(Arc<str>),
17 Compiled(Arc<[u8]>),
18 Both {
19 source: Arc<str>,
20 compiled: Arc<[u8]>,
21 },
22 ContentAddressed {
25 manifest_bytes: Arc<[u8]>,
27 blob_cache: Arc<HashMap<[u8; 32], Vec<u8>>>,
30 },
31}
32
33impl ModuleCode {
34 pub fn source(&self) -> Option<&str> {
36 match self {
37 Self::Source(source) => Some(source),
38 Self::Compiled(_) => None,
39 Self::Both { source, .. } => Some(source),
40 Self::ContentAddressed { .. } => None,
41 }
42 }
43
44 pub fn compiled(&self) -> Option<&[u8]> {
46 match self {
47 Self::Source(_) => None,
48 Self::Compiled(compiled) => Some(compiled),
49 Self::Both { compiled, .. } => Some(compiled),
50 Self::ContentAddressed { .. } => None,
51 }
52 }
53
54 pub fn manifest_bytes(&self) -> Option<&[u8]> {
56 match self {
57 Self::ContentAddressed { manifest_bytes, .. } => Some(manifest_bytes),
58 _ => None,
59 }
60 }
61
62 pub fn blob_cache(&self) -> Option<&HashMap<[u8; 32], Vec<u8>>> {
64 match self {
65 Self::ContentAddressed { blob_cache, .. } => Some(blob_cache),
66 _ => None,
67 }
68 }
69}
70
71#[derive(Debug, Clone)]
73pub struct ResolvedModuleArtifact {
74 pub module_path: String,
75 pub code: ModuleCode,
76 pub origin_path: Option<PathBuf>,
77}
78
79pub trait ModuleResolver {
81 fn resolve(
82 &self,
83 module_path: &str,
84 context_path: Option<&Path>,
85 ) -> Result<Option<ResolvedModuleArtifact>>;
86
87 fn list_modules(&self) -> Result<Vec<String>> {
88 Ok(Vec::new())
89 }
90}
91
92#[derive(Debug, Clone, Default)]
94pub struct InMemoryResolver {
95 modules: HashMap<String, ModuleCode>,
96}
97
98impl InMemoryResolver {
99 pub fn register(&mut self, module_path: impl Into<String>, code: ModuleCode) {
100 self.modules.insert(module_path.into(), code);
101 }
102
103 pub fn clear(&mut self) {
104 self.modules.clear();
105 }
106
107 pub fn has(&self, module_path: &str) -> bool {
108 self.modules.contains_key(module_path)
109 }
110
111 pub fn module_paths(&self) -> Vec<String> {
112 let mut items: Vec<String> = self.modules.keys().cloned().collect();
113 items.sort();
114 items
115 }
116}
117
118impl ModuleResolver for InMemoryResolver {
119 fn resolve(
120 &self,
121 module_path: &str,
122 _context_path: Option<&Path>,
123 ) -> Result<Option<ResolvedModuleArtifact>> {
124 Ok(self
125 .modules
126 .get(module_path)
127 .cloned()
128 .map(|code| ResolvedModuleArtifact {
129 module_path: module_path.to_string(),
130 code,
131 origin_path: None,
132 }))
133 }
134
135 fn list_modules(&self) -> Result<Vec<String>> {
136 Ok(self.module_paths())
137 }
138}
139
140pub struct FilesystemResolver<'a> {
142 pub stdlib_path: &'a Path,
143 pub module_paths: &'a [PathBuf],
144 pub dependency_paths: &'a HashMap<String, PathBuf>,
145}
146
147impl<'a> ModuleResolver for FilesystemResolver<'a> {
148 fn resolve(
149 &self,
150 module_path: &str,
151 context_path: Option<&Path>,
152 ) -> Result<Option<ResolvedModuleArtifact>> {
153 let resolved = super::resolution::resolve_module_path_with_context(
154 module_path,
155 context_path,
156 self.stdlib_path,
157 self.module_paths,
158 self.dependency_paths,
159 )?;
160
161 let source = std::fs::read_to_string(&resolved).map_err(|e| ShapeError::ModuleError {
162 message: format!("Failed to read module file: {}: {}", resolved.display(), e),
163 module_path: Some(resolved.clone()),
164 })?;
165
166 Ok(Some(ResolvedModuleArtifact {
167 module_path: module_path.to_string(),
168 code: ModuleCode::Source(Arc::from(source)),
169 origin_path: Some(resolved),
170 }))
171 }
172}