1use std::collections::HashMap;
2
3use nu_protocol::engine::{EngineState, StateWorkingSet, VirtualPath};
4
5use crate::store::Store;
6
7pub fn load_modules(
15 engine_state: &mut EngineState,
16 store: &Store,
17 modules: &HashMap<String, ssri::Integrity>,
18) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
19 for (topic, hash) in modules {
20 let name = match topic.strip_suffix(".nu") {
21 Some(n) if !n.is_empty() => n,
22 _ => continue,
23 };
24 let content_bytes = store.cas_read_sync(hash)?;
25 let content = String::from_utf8(content_bytes)?;
26 register_module(engine_state, name, &content)?;
27 }
28 Ok(())
29}
30
31fn register_module(
44 engine_state: &mut EngineState,
45 name: &str,
46 content: &str,
47) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
48 let module_path = name.replace('.', "/");
49
50 let mut working_set = StateWorkingSet::new(engine_state);
51
52 let virt_file_name = format!("{module_path}/mod.nu");
54 let file_id = working_set.add_file(virt_file_name.clone(), content.as_bytes());
55 let virt_file_id = working_set.add_virtual_path(virt_file_name, VirtualPath::File(file_id));
56
57 let segments: Vec<&str> = module_path.split('/').collect();
62 let mut child_id = virt_file_id;
63
64 for depth in (0..segments.len()).rev() {
65 let dir_path = if depth == 0 {
66 segments[0].to_string()
67 } else {
68 segments[..=depth].join("/")
69 };
70 child_id = working_set.add_virtual_path(dir_path, VirtualPath::Dir(vec![child_id]));
71 }
72
73 engine_state.merge_delta(working_set.render())?;
74
75 tracing::debug!("Registered VFS module: {}", module_path);
76
77 Ok(())
78}