1use std::path::Path;
8
9use nvim_oxi::Dictionary;
10use nvim_oxi::Object;
11use nvim_oxi::conversion::FromObject;
12use nvim_oxi::lua::Poppable;
13use nvim_oxi::lua::ffi::State;
14use nvim_oxi::serde::Deserializer;
15use serde::Deserialize;
16use ytil_noxi::buffer::BufferExt;
17
18pub fn dict() -> Dictionary {
20 dict! {
21 "run_test": fn_from!(run_test),
22 }
23}
24
25#[derive(Clone, Copy, Deserialize)]
26enum TargetTerminal {
27 WezTerm,
28 Nvim,
29}
30
31impl FromObject for TargetTerminal {
32 fn from_object(obj: Object) -> Result<Self, nvim_oxi::conversion::Error> {
33 Self::deserialize(Deserializer::new(obj)).map_err(Into::into)
34 }
35}
36
37impl Poppable for TargetTerminal {
38 unsafe fn pop(lstate: *mut State) -> Result<Self, nvim_oxi::lua::Error> {
39 unsafe {
40 let obj = Object::pop(lstate)?;
41 Self::from_object(obj).map_err(nvim_oxi::lua::Error::pop_error_from_err::<Self, _>)
42 }
43 }
44}
45
46fn run_test(target_terminal: TargetTerminal) -> Option<()> {
47 let file_path = ytil_noxi::buffer::get_absolute_path(Some(&nvim_oxi::api::get_current_buf()))?;
48
49 let test_name = ytil_noxi::tree_sitter::get_enclosing_fn_name_of_position(&file_path)?;
50
51 let test_runner = get_test_runner_for_path(&file_path)
52 .inspect_err(|err| {
53 ytil_noxi::notify::error(format!(
54 "error getting test runner | path={} error={err:#?}",
55 file_path.display()
56 ));
57 })
58 .ok()?;
59
60 match target_terminal {
61 TargetTerminal::WezTerm => run_test_in_wezterm(test_runner, &test_name),
62 TargetTerminal::Nvim => run_test_in_nvim_term(test_runner, &test_name),
63 }
64}
65
66fn run_test_in_wezterm(test_runner: &str, test_name: &str) -> Option<()> {
67 let cur_pane_id = ytil_wezterm::get_current_pane_id()
68 .inspect_err(|err| ytil_noxi::notify::error(format!("error getting current WezTerm pane id | error={err:#?}")))
69 .ok()?;
70
71 let wez_panes = ytil_wezterm::get_all_panes(&[])
72 .inspect_err(|err| {
73 ytil_noxi::notify::error(format!("error getting WezTerm panes | error={err:#?}"));
74 })
75 .ok()?;
76
77 let Some(cur_pane) = wez_panes.iter().find(|p| p.pane_id == cur_pane_id) else {
78 ytil_noxi::notify::error(format!(
79 "error WezTerm pane not found | pane_id={cur_pane_id:#?} panes={wez_panes:#?}"
80 ));
81 return None;
82 };
83
84 let Some(test_runner_pane) = wez_panes.iter().find(|p| p.is_sibling_terminal_pane_of(cur_pane)) else {
85 ytil_noxi::notify::error(format!(
86 "error finding sibling pane to run test | current_pane={cur_pane:#?} panes={wez_panes:#?} test={test_name}"
87 ));
88 return None;
89 };
90
91 let test_run_cmd = format!("'{test_runner} {test_name}'");
92
93 let send_text_to_pane_cmd = ytil_wezterm::send_text_to_pane_cmd(&test_run_cmd, test_runner_pane.pane_id);
94 let submit_pane_cmd = ytil_wezterm::submit_pane_cmd(test_runner_pane.pane_id);
95
96 ytil_cmd::silent_cmd("sh")
97 .args(["-c", &format!("{send_text_to_pane_cmd} && {submit_pane_cmd}")])
98 .spawn()
99 .inspect_err(|err| {
100 ytil_noxi::notify::error(format!(
101 "error executing test run cmd | cmd={test_run_cmd:#?} pane={test_runner_pane:#?} error={err:#?}"
102 ));
103 })
104 .ok()?;
105
106 Some(())
107}
108
109fn run_test_in_nvim_term(test_runner: &str, test_name: &str) -> Option<()> {
110 let Some(terminal_buffer) = nvim_oxi::api::list_bufs().find(BufferExt::is_terminal) else {
111 ytil_noxi::notify::error(format!(
112 "error no terminal buffer found | test_runner={test_runner:?} test_name={test_name:?}",
113 ));
114 return None;
115 };
116
117 terminal_buffer.send_command(&format!("{test_runner} {test_name}\n"));
118
119 Some(())
120}
121
122fn get_test_runner_for_path(path: &Path) -> color_eyre::Result<&'static str> {
136 let git_repo_root = ytil_git::repo::get_root(&ytil_git::repo::discover(path)?);
137
138 if std::fs::read_dir(git_repo_root)?.any(|res| {
139 res.as_ref()
140 .map(|de| de.file_name() == "Makefile.toml")
141 .unwrap_or(false)
142 }) {
143 return Ok("cargo make test");
144 }
145
146 Ok("cargo test")
147}