1use std::{collections::BTreeMap, sync::Arc};
2
3use huff_neo_utils::file::file_source::FileSource;
4use huff_neo_utils::file::full_file_source::OutputLocation;
5use huff_neo_utils::prelude::Artifact;
6use walkdir::WalkDir;
7
8pub fn get_cached_artifacts(
10 files: &[Arc<FileSource>],
11 out: &OutputLocation,
12 constructor_args: &[String],
13 constant_overrides: &BTreeMap<String, String>,
14) -> Option<Vec<Arc<Artifact>>> {
15 let artifacts: Vec<(Arc<FileSource>, Artifact)> = resolve_existing_artifacts(files, out, constructor_args, constant_overrides)?;
18
19 Some(artifacts.into_iter().map(|(_, artifact)| Arc::new(artifact)).collect())
21}
22
23pub fn resolve_existing_artifacts(
25 files: &[Arc<FileSource>],
26 output: &OutputLocation,
27 constructor_args: &[String],
28 constant_overrides: &BTreeMap<String, String>,
29) -> Option<Vec<(Arc<FileSource>, Artifact)>> {
30 let mut artifacts: Vec<(Arc<FileSource>, Artifact)> = Vec::new();
31
32 let mut file_sources: std::collections::HashMap<String, Arc<FileSource>> =
34 files.iter().map(|f| (f.path.clone().to_lowercase(), Arc::clone(f))).collect();
35
36 let output_dir = if !output.0.is_empty() { &*output.0 } else { "./artifacts" };
38
39 tracing::debug!(target: "core", "Traversing output directory {}", output_dir);
41 let artifact_files = WalkDir::new(output_dir)
43 .into_iter()
44 .filter_map(Result::ok)
45 .filter(|e| e.file_type().is_file() && e.path().extension().is_some_and(|ext| ext == "json"));
46 for entry in artifact_files {
47 let formatted_path = entry.path().with_extension("").display().to_string().replace(output_dir, ".").to_lowercase();
49 let expected = file_sources.remove(&formatted_path);
50
51 match serde_json::from_str::<Artifact>(&std::fs::read_to_string(entry.path()).unwrap()) {
53 Ok(artifact) => {
54 match expected {
56 Some(expected_fs) => {
57 if artifact.constructor_args != constructor_args {
58 tracing::warn!(target: "core", "Mismatched Constructor Args for Cached Artifact \"{}\"", artifact.file.path);
59 return None;
60 }
61 if &artifact.constant_overrides != constant_overrides {
62 tracing::warn!(target: "core", "Mismatched Constant Overrides for Cached Artifact \"{}\"", artifact.file.path);
63 return None;
64 }
65 if artifact.file.source != expected_fs.source {
66 tracing::warn!(target: "core", "Cache Resolution Failed: \"{}\" Artifact Outdated", artifact.file.path);
67 return None;
68 } else {
69 artifacts.push((expected_fs, artifact));
70 }
71 }
72 None => {
73 tracing::warn!(target: "core", "Cache Resolution Found Unexpected Artifact: \"{}\"", artifact.file.path)
74 }
75 }
76 }
77 Err(e) => {
78 tracing::debug!(target: "core", "Not an artifact file: {}", e);
81 if expected.is_some() {
82 tracing::error!(target: "core", "Expected artifact file to be compiled: {}", entry.path().display());
83 return None;
84 }
85 }
86 }
87 }
88
89 match file_sources.is_empty() {
90 true => Some(artifacts),
91 false => {
92 tracing::warn!(target: "core", "Cache Resolution Failed: Missing Artifact Files");
93 None
94 }
95 }
96}