harn_modules/
construction.rs1use std::collections::HashMap;
4use std::path::{Path, PathBuf};
5
6use harn_parser::SNode;
7
8use super::{
9 build_inner, normalize_path, ModuleGraph, ModuleGraphBuild, PackageContext,
10 ParsedSourceRetention,
11};
12
13pub fn build(files: &[PathBuf]) -> ModuleGraph {
19 build_inner(
20 files,
21 ParsedSourceRetention::None,
22 None,
23 PackageContext::Project,
24 None,
25 )
26 .graph
27}
28
29pub fn build_with_parsed_sources(files: &[PathBuf]) -> ModuleGraphBuild {
35 let parsed_source_targets = files.iter().map(|file| normalize_path(file)).collect();
36 build_inner(
37 files,
38 ParsedSourceRetention::Seeds(&parsed_source_targets),
39 None,
40 PackageContext::Project,
41 None,
42 )
43}
44
45pub fn build_closed_program(files: &[PathBuf]) -> ModuleGraphBuild {
51 build_inner(
52 files,
53 ParsedSourceRetention::All,
54 None,
55 PackageContext::Project,
56 None,
57 )
58}
59
60pub fn build_with_module_projection<T: Send>(
69 files: &[PathBuf],
70 project: impl Fn(&Path, &str, Option<&[SNode]>) -> T + Sync,
71) -> (ModuleGraph, HashMap<PathBuf, T>) {
72 let projections = std::sync::Mutex::new(HashMap::new());
73 let observer = |path: &Path, source: &str, program: Option<&[SNode]>| {
74 let projection = project(path, source, program);
75 projections
76 .lock()
77 .expect("module projection lock poisoned")
78 .insert(path.to_path_buf(), projection);
79 };
80 let graph = build_inner(
81 files,
82 ParsedSourceRetention::None,
83 None,
84 PackageContext::Project,
85 Some(&observer),
86 )
87 .graph;
88 (
89 graph,
90 projections
91 .into_inner()
92 .expect("module projection lock poisoned"),
93 )
94}
95
96pub fn build_for_reference_index(
104 files: &[PathBuf],
105 source_overrides: Option<&HashMap<PathBuf, String>>,
106) -> ModuleGraphBuild {
107 build_inner(
108 files,
109 ParsedSourceRetention::All,
110 source_overrides,
111 PackageContext::Project,
112 None,
113 )
114}
115
116#[cfg(test)]
117mod tests {
118 use super::*;
119
120 #[test]
121 fn projection_and_graph_share_the_parsed_source_snapshot() {
122 let dir = tempfile::tempdir().unwrap();
123 let paths = (0..12)
124 .map(|index| {
125 let path = dir.path().join(format!("module{index}.harn"));
126 std::fs::write(&path, "pub fn original() { 1 }\n").unwrap();
127 path
128 })
129 .collect::<Vec<_>>();
130 let observed = std::sync::atomic::AtomicUsize::new(0);
131 let (graph, projections) = build_with_module_projection(&paths, |path, source, program| {
132 observed.fetch_add(1, std::sync::atomic::Ordering::Relaxed);
133 assert_eq!(program.unwrap().len(), 1);
134 std::fs::write(path, "pub fn replacement() { 2 }\n").unwrap();
136 source.to_owned()
137 });
138 assert_eq!(
139 observed.load(std::sync::atomic::Ordering::Relaxed),
140 paths.len()
141 );
142 assert_eq!(graph.module_paths().len(), paths.len());
143 assert_eq!(projections.len(), paths.len());
144 for path in &paths {
145 assert_eq!(
146 projections[&normalize_path(path)],
147 "pub fn original() { 1 }\n"
148 );
149 assert_eq!(
150 graph.exported_kind(path, "original"),
151 Some(super::super::DefKind::Function)
152 );
153 assert_eq!(graph.exported_kind(path, "replacement"), None);
154 }
155 let refreshed = build(&paths);
156 for path in &paths {
157 assert_eq!(refreshed.exported_kind(path, "original"), None);
158 assert_eq!(
159 refreshed.exported_kind(path, "replacement"),
160 Some(super::super::DefKind::Function)
161 );
162 }
163 }
164}