1use crate::Error;
4use std::{
5 collections::HashMap,
6 fs,
7 io::{Read, Write},
8 path::{Path, PathBuf},
9};
10
11pub fn replace_in_file(file_path: PathBuf, replacements: HashMap<&str, &str>) -> Result<(), Error> {
19 let mut file_content = String::new();
21 fs::File::open(&file_path)?.read_to_string(&mut file_content)?;
22 let mut modified_content = file_content;
24 for (target, replacement) in &replacements {
25 modified_content = modified_content.replace(target, replacement);
26 }
27 let mut file = fs::File::create(&file_path)?;
29 file.write_all(modified_content.as_bytes())?;
30 Ok(())
31}
32
33pub fn get_project_name_from_path<'a>(path: &'a Path, default: &'a str) -> &'a str {
40 path.file_name().and_then(|name| name.to_str()).unwrap_or(default)
41}
42
43pub fn get_relative_or_absolute_path(base: &Path, full: &Path) -> PathBuf {
50 match full.strip_prefix(base) {
51 Ok(relative) => relative.to_path_buf(),
52 Err(_) => full.to_path_buf(),
54 }
55}
56
57pub fn with_current_dir<F, R>(dir: &Path, f: F) -> anyhow::Result<R>
59where
60 F: FnOnce() -> anyhow::Result<R>,
61{
62 let original_dir = std::env::current_dir()?;
63 std::env::set_current_dir(dir)?;
64 let result = f();
65 std::env::set_current_dir(original_dir)?;
66 result
67}
68
69pub async fn with_current_dir_async<F, R>(dir: &Path, f: F) -> anyhow::Result<R>
71where
72 F: AsyncFnOnce() -> anyhow::Result<R>,
73{
74 let original_dir = std::env::current_dir()?;
75 std::env::set_current_dir(dir)?;
76 let result = f().await;
77 std::env::set_current_dir(original_dir)?;
78 result
79}
80
81#[cfg(test)]
82mod tests {
83 use super::*;
84 use anyhow::Result;
85 use std::{
86 fs,
87 sync::{Mutex, OnceLock},
88 };
89
90 static CWD_TEST_MUTEX: OnceLock<Mutex<()>> = OnceLock::new();
93
94 fn cwd_lock() -> std::sync::MutexGuard<'static, ()> {
95 CWD_TEST_MUTEX.get_or_init(|| Mutex::new(())).lock().unwrap()
96 }
97
98 #[test]
99 fn test_replace_in_file() -> Result<(), Error> {
100 let temp_dir = tempfile::tempdir()?;
101 let file_path = temp_dir.path().join("file.toml");
102 let mut file = fs::File::create(temp_dir.path().join("file.toml"))?;
103 writeln!(file, "name = test, version = 5.0.0")?;
104 let mut replacements_in_cargo = HashMap::new();
105 replacements_in_cargo.insert("test", "changed_name");
106 replacements_in_cargo.insert("5.0.0", "5.0.1");
107 replace_in_file(file_path.clone(), replacements_in_cargo)?;
108 let content = fs::read_to_string(file_path).expect("Could not read file");
109 assert_eq!(content.trim(), "name = changed_name, version = 5.0.1");
110 Ok(())
111 }
112
113 #[test]
114 fn get_project_name_from_path_works() -> Result<(), Error> {
115 let path = Path::new("./path/to/project/my-parachain");
116 assert_eq!(get_project_name_from_path(path, "default_name"), "my-parachain");
117 Ok(())
118 }
119
120 #[test]
121 fn get_project_name_from_path_default_value() -> Result<(), Error> {
122 let path = Path::new("./");
123 assert_eq!(get_project_name_from_path(path, "my-contract"), "my-contract");
124 Ok(())
125 }
126
127 #[test]
128 fn get_relative_or_absolute_path_works() {
129 [
130 ("/path/to/project", "/path/to/project", ""),
131 ("/path/to/project", "/path/to/src", "/path/to/src"),
132 ("/path/to/project", "/path/to/project/main.rs", "main.rs"),
133 ("/path/to/project", "/path/to/project/../main.rs", "../main.rs"),
134 ("/path/to/project", "/path/to/project/src/main.rs", "src/main.rs"),
135 ]
136 .into_iter()
137 .for_each(|(base, full, expected)| {
138 assert_eq!(
139 get_relative_or_absolute_path(Path::new(base), Path::new(full)),
140 Path::new(expected)
141 );
142 });
143 }
144
145 #[test]
146 fn with_current_dir_changes_and_restores_cwd() -> anyhow::Result<()> {
147 let _guard = cwd_lock();
148 let original = std::env::current_dir()?;
149 let temp_dir = tempfile::tempdir()?;
150 let tmp_path = temp_dir.path().to_path_buf();
151
152 let res: &str = with_current_dir(&tmp_path, || {
153 let cwd = std::env::current_dir().unwrap();
156 assert_eq!(cwd.canonicalize().unwrap(), tmp_path.canonicalize().unwrap());
157 fs::write("hello.txt", b"world").unwrap();
159 Ok("done")
160 })?;
161 assert_eq!(res, "done");
162
163 assert_eq!(std::env::current_dir()?, original);
165 assert!(tmp_path.join("hello.txt").exists());
167 Ok(())
168 }
169
170 #[tokio::test]
171 async fn with_current_dir_async_changes_and_restores_cwd() -> anyhow::Result<()> {
172 {
174 let _guard = cwd_lock();
175 }
176
177 let original = std::env::current_dir()?;
178 let temp_dir = tempfile::tempdir()?;
179 let tmp_path = temp_dir.path().to_path_buf();
180
181 let res: &str = with_current_dir_async(&tmp_path, || async {
182 let cwd = std::env::current_dir().unwrap();
185 assert_eq!(cwd.canonicalize().unwrap(), tmp_path.canonicalize().unwrap());
186 fs::write("async.txt", b"ok").unwrap();
188 Ok("async-done")
189 })
190 .await?;
191 assert_eq!(res, "async-done");
192
193 assert_eq!(std::env::current_dir()?, original);
195 assert!(tmp_path.join("async.txt").exists());
197 Ok(())
198 }
199}