unity_solution_generator/
paths.rs1use std::ffi::{CStr, CString};
2use std::path::Path;
3
4pub const DEFAULT_GENERATOR_ROOT: &str = "Library/UnitySolutionGenerator";
5
6pub fn parent_directory(path: &str) -> &str {
7 match path.rfind('/') {
8 Some(i) => &path[..i],
9 None => "",
10 }
11}
12
13pub fn join_path(base: &str, component: &str) -> String {
14 if base.ends_with('/') {
15 format!("{}{}", base, component)
16 } else {
17 format!("{}/{}", base, component)
18 }
19}
20
21pub fn resolve_real_path(path: &str) -> String {
23 let Ok(c) = CString::new(path) else {
24 return path.to_string();
25 };
26 unsafe {
27 let resolved = libc::realpath(c.as_ptr(), std::ptr::null_mut());
28 if resolved.is_null() {
29 return path.to_string();
30 }
31 let s = CStr::from_ptr(resolved).to_string_lossy().into_owned();
32 libc::free(resolved as *mut libc::c_void);
33 s
34 }
35}
36
37pub fn resolve_project_root(path: &str) -> String {
41 let resolved = resolve_real_path(path);
42 let mut current = resolved.as_str();
43 while !current.is_empty() && current != "/" {
44 let marker = join_path(current, "ProjectSettings/ProjectVersion.txt");
45 if Path::new(&marker).exists() {
46 return current.to_string();
47 }
48 current = parent_directory(current);
49 }
50 resolved
51}
52
53pub fn lockfile_path(project_root: &str, generator_root: &str) -> String {
54 join_path(project_root, &format!("{}/csproj.lock", generator_root))
55}
56
57pub fn usg_cache_dir(unity_version: &str) -> String {
63 let cache_home = std::env::var("XDG_CACHE_HOME")
64 .ok()
65 .filter(|s| !s.is_empty())
66 .or_else(|| std::env::var("HOME").ok().map(|h| format!("{}/.cache", h)))
67 .expect("usg_cache_dir: neither XDG_CACHE_HOME nor HOME is set");
68 format!("{}/unity-solution-generator/{}", cache_home, unity_version)
69}