huff_neo_utils/file/
remapper.rs1use itertools::Itertools;
2use serde::{Deserialize, Serialize};
3use std::collections::HashMap;
4use std::fs;
5use std::io::{BufReader, Read};
6use std::path::{Path, PathBuf};
7
8#[derive(Debug, Default, Serialize, Deserialize, PartialEq, Eq, Clone)]
10pub struct Remapper {
11 pub remappings: HashMap<String, String>,
13 pub base_dir: String,
15}
16
17impl Remapper {
18 pub fn new(root: impl AsRef<str>) -> Self {
21 let mut inner = HashMap::<String, String>::new();
22
23 Remapper::from_foundry(root.as_ref(), &mut inner);
25
26 Remapper::from_file(root.as_ref(), &mut inner);
28
29 Remapper::from_libs(root.as_ref(), &mut inner);
31
32 Self { remappings: inner, base_dir: root.as_ref().to_string() }
34 }
35
36 pub fn split(remapping: &str) -> Option<(String, String)> {
38 let mut split = remapping.splitn(2, '=');
39 match split.next() {
40 Some(from) => split.next().map(|to| (from.to_string(), to.to_string())),
41 None => None,
42 }
43 }
44
45 pub fn from_foundry(root: &str, inner: &mut HashMap<String, String>) {
47 let path = Path::new(root).join("foundry.toml");
49
50 match fs::File::open(&path) {
51 Ok(f) => {
52 let mut data = String::new();
54 let mut br = BufReader::new(f);
55
56 if let Err(e) = br.read_to_string(&mut data) {
58 tracing::warn!(target: "parser", "Failed to read \"foundry.toml\" file contents!\nError: {:?}", e);
59 return;
60 }
61
62 let toml = match toml::from_str::<toml::Value>(&data) {
64 Ok(t) => t,
65 Err(e) => {
66 tracing::warn!(target: "parser", "\"foundry.toml\" incorrectly formatted: {:?}", e);
67 return;
68 }
69 };
70
71 let toml_map = toml.as_table().cloned().unwrap_or_else(toml::map::Map::new);
73
74 let profiles = toml_map.iter().filter_map(|p| p.1.as_table()).collect::<Vec<&toml::map::Map<String, toml::Value>>>();
76 let unwrapped_profiles = profiles.iter().flat_map(|t| t.values().collect_vec()).collect::<Vec<&toml::Value>>();
77
78 let inner_tables =
80 unwrapped_profiles.iter().filter_map(|t| t.as_table()).collect::<Vec<&toml::map::Map<String, toml::Value>>>();
81 let unwrapped_inner_tables = inner_tables
82 .iter()
83 .flat_map(|t| t.into_iter().filter(|m| m.0.eq("remappings")).map(|m| m.1).collect_vec())
84 .collect::<Vec<&toml::Value>>();
85
86 let arr_mappings = unwrapped_inner_tables.iter().filter_map(|t| t.as_array()).collect::<Vec<&Vec<toml::Value>>>();
88 let unwrapped_mappings = arr_mappings.iter().cloned().flatten().collect::<Vec<&toml::Value>>();
89
90 let remapping_strings = unwrapped_mappings.iter().filter_map(|t| t.as_str()).collect::<Vec<&str>>();
92
93 remapping_strings.iter().for_each(|remapping| {
95 match Remapper::split(remapping) {
96 Some((from, to)) => {
97 inner.insert(from, to);
98 }
99 None => tracing::warn!(target: "parser", "Failed to split remapping using \"=\" at \"{}\" in \"{}\"!", remapping, path.to_string_lossy()),
100 }
101 });
102 }
103 Err(e) => {
104 tracing::warn!(target: "parser", "Foundry.toml not found in specified \"{}\"", root);
105 tracing::warn!(target: "parser", "{:?}", e);
106 }
107 }
108 }
109
110 pub fn from_libs(root: &str, inner: &mut HashMap<String, String>) {
113 let Ok(entries) = fs::read_dir(Path::new(root).join("lib")) else {
114 return;
115 };
116 for entry in entries.filter_map(Result::ok).filter(|entry| entry.path().is_dir()) {
117 let name = entry.file_name().to_string_lossy().into_owned();
118 if name.starts_with('.') || inner.contains_key(&name) || inner.contains_key(&format!("{name}/")) {
119 continue;
120 }
121 let to = if entry.path().join("src").is_dir() { format!("lib/{name}/src/") } else { format!("lib/{name}/") };
122 inner.insert(format!("{name}/"), to);
123 }
124 }
125
126 pub fn from_file(root: &str, inner: &mut HashMap<String, String>) {
128 let mut remappings: HashMap<String, String> = HashMap::new();
129 let remappings_file = PathBuf::new().join(root).join("remappings.txt");
130 if remappings_file.is_file() {
131 let content = fs::read_to_string(remappings_file).map_err(|err| err.to_string()).unwrap();
132
133 let rem_lines = content.lines().collect::<Vec<&str>>();
134 let rem = rem_lines.iter().filter(|l| l != &&"").map(|l| l.split_once('=')).collect::<Vec<Option<(&str, &str)>>>();
135 rem.iter().for_each(|pair| {
136 if let Some((lib, path)) = pair {
137 remappings.insert(lib.to_string(), path.to_string());
138 }
139 });
140
141 inner.extend(remappings);
142 }
143 }
144}
145
146impl Remapper {
147 pub fn remap(&self, path: &str) -> Option<String> {
149 let (from, to) = self.remappings.iter().filter(|(from, _)| path.starts_with(from.as_str())).max_by_key(|(from, _)| from.len())?;
150 tracing::debug!(target: "parser", "found key {} and value {}", from, to);
151 Some(format!("{}{to}{}", self.base_dir, &path[from.len()..]))
152 }
153}