1use std::path::Path;
2use std::path::PathBuf;
3use std::process::Command;
4
5use owo_colors::OwoColorize;
6
7const BINS: &[&str] = &[
10 "agg",
11 "ags",
12 "catl",
13 "fkr",
14 "gbm",
15 "gch",
16 "gcu",
17 "ghl",
18 "idt",
19 "muxr",
20 "muxr-server",
21 "oe",
22 "rmr",
23 "strgci",
24 "tec",
25 "try",
26 "vpg",
27 "yghfl",
28 "yhfp",
29 "zcp",
30 "zj",
31 "znt",
32 "zop",
33];
34const LIBS: &[(&str, &str)] = &[("libnvrim.dylib", "nvrim.so")];
36const BINS_DEFAULT_PATH: &[&str] = &[".local", "bin"];
38const NVIM_LIBS_DEFAULT_PATH: &[&str] = &[".config", "nvim", "lua"];
40
41pub fn run(args: &mut Vec<String>) -> rootcause::Result<()> {
42 let is_debug = drop_element(args, "--debug");
43 let bins_path = args.first().cloned().map_or_else(
44 || ytil_sys::dir::build_home_path(BINS_DEFAULT_PATH),
45 |supplied_bins_path| Ok(PathBuf::from(supplied_bins_path)),
46 )?;
47 let cargo_target_path = args.get(1).cloned().map_or_else(
48 || {
49 std::env::var("CARGO_MANIFEST_DIR").map(|cargo_manifest_dir| {
50 let mut x = PathBuf::from(cargo_manifest_dir);
51 remove_last_n_dirs(&mut x, 2);
52 x.join("target")
53 })
54 },
55 |x| Ok(PathBuf::from(x)),
56 )?;
57 let nvim_libs_path = args.get(2).cloned().map_or_else(
58 || ytil_sys::dir::build_home_path(NVIM_LIBS_DEFAULT_PATH),
59 |supplied_nvim_libs_path| Ok(PathBuf::from(supplied_nvim_libs_path)),
60 )?;
61
62 let (cargo_target_location, build_profile) = if is_debug {
63 (cargo_target_path.join("debug"), None)
64 } else {
65 (cargo_target_path.join("release"), Some("--release"))
66 };
67
68 ytil_cmd::silent_cmd("cargo").args(["fmt"]).status()?.exit_ok()?;
69
70 if !is_debug {
72 ytil_cmd::silent_cmd("cargo")
73 .args(["clippy", "--all-targets", "--all-features", "--", "-D", "warnings"])
74 .status()?
75 .exit_ok()?;
76 }
77
78 ytil_cmd::silent_cmd("cargo")
79 .args([Some("build"), build_profile].into_iter().flatten())
80 .status()?
81 .exit_ok()?;
82
83 for bin in BINS {
84 cp(&cargo_target_location.join(bin), &bins_path.join(bin))?;
85 }
86
87 for (source_lib_name, target_lib_name) in LIBS {
88 cp(
89 &cargo_target_location.join(source_lib_name),
90 &nvim_libs_path.join(target_lib_name),
91 )?;
92 }
93
94 let mut zj = Command::new(bins_path.join("zj"));
95 zj.arg("install");
96 if is_debug {
97 zj.arg("--debug");
98 }
99 zj.status()?.exit_ok()?;
100
101 Command::new(bins_path.join("gbm")).arg("install").status()?.exit_ok()?;
102
103 Ok(())
104}
105
106fn remove_last_n_dirs(path: &mut PathBuf, n: usize) {
108 for _ in 0..n {
109 if !path.pop() {
110 return;
111 }
112 }
113}
114
115fn drop_element<T, U: ?Sized>(vec: &mut Vec<T>, target: &U) -> bool
118where
119 T: PartialEq<U>,
120{
121 let Some(idx) = vec.iter().position(|x| x == target) else {
122 return false;
123 };
124 vec.swap_remove(idx);
125 true
126}
127
128fn cp(from: &Path, to: &Path) -> rootcause::Result<()> {
135 ytil_sys::file::atomic_cp(from, to)?;
136 println!("{} {} to {}", "Copied".green().bold(), from.display(), to.display());
137 Ok(())
138}
139
140#[cfg(test)]
141mod tests {
142 use std::path::PathBuf;
143
144 use rstest::rstest;
145
146 use crate::local::drop_element;
147 use crate::local::remove_last_n_dirs;
148
149 #[test]
150 fn test_drop_element_returns_true_and_removes_the_element_from_the_vec() {
151 let mut input = vec![42, 7];
152 assert!(drop_element(&mut input, &7));
153 assert_eq!(input, vec![42]);
154 }
155
156 #[test]
157 fn test_drop_element_returns_false_and_does_nothing_to_a_non_empty_vec() {
158 let mut input = vec![42, 7];
159 assert!(!drop_element(&mut input, &3));
160 assert_eq!(input, vec![42, 7]);
161 }
162
163 #[test]
164 fn test_drop_element_returns_false_and_does_nothing_to_an_empty_vec() {
165 let mut input: Vec<usize> = vec![];
166 assert!(!drop_element(&mut input, &3));
167 assert!(input.is_empty());
168 }
169
170 #[rstest]
171 #[case::no_dirs_removed(PathBuf::from("/home/user/docs"), 0, PathBuf::from("/home/user/docs"))]
172 #[case::remove_one_dir(PathBuf::from("/home/user/docs"), 1, PathBuf::from("/home/user"))]
173 #[case::remove_more_than_exist(PathBuf::from("/home/user"), 5, PathBuf::from("/"))]
174 #[case::root_path(PathBuf::from("/"), 1, PathBuf::from("/"))]
175 #[case::empty_path(PathBuf::new(), 1, PathBuf::new())]
176 fn test_remove_last_n_dirs_when_requested_count_varies_updates_path(
177 #[case] mut initial: PathBuf,
178 #[case] n: usize,
179 #[case] expected: PathBuf,
180 ) {
181 remove_last_n_dirs(&mut initial, n);
182 pretty_assertions::assert_eq!(initial, expected);
183 }
184}