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