1use std::process::Command;
2use std::time::Duration;
3use std::time::Instant;
4
5use color_eyre::owo_colors::OwoColorize as _;
6use ytil_cmd::Cmd;
7use ytil_cmd::CmdError;
8use ytil_cmd::CmdExt as _;
9
10pub mod bash_language_server;
11pub mod commitlint;
12pub mod deno;
13pub mod docker_langserver;
14pub mod eslint_d;
15pub mod graphql_lsp;
16pub mod hadolint;
17pub mod harper_ls;
18pub mod helm_ls;
19pub mod lua_ls;
20pub mod marksman;
21pub mod nvim;
22pub mod prettierd;
23pub mod quicktype;
24pub mod ruff_lsp;
25pub mod rust_analyzer;
26pub mod shellcheck;
27pub mod sql_language_server;
28pub mod sqruff;
29pub mod taplo;
30pub mod terraform_ls;
31pub mod typescript_language_server;
32pub mod typos_lsp;
33pub mod vscode_langservers;
34pub mod yaml_language_server;
35
36pub trait Installer: Sync + Send {
38 fn bin_name(&self) -> &'static str;
40
41 fn install(&self) -> color_eyre::Result<()>;
43
44 fn check(&self) -> Option<color_eyre::Result<String>> {
46 let check_args = self.check_args()?;
47 let mut cmd = Command::new(self.bin_name());
48 cmd.args(check_args);
49
50 let check_res = cmd
51 .exec()
52 .and_then(|output| {
53 std::str::from_utf8(&output.stdout)
54 .map(ToOwned::to_owned)
55 .map_err(|err| CmdError::Utf8 {
56 cmd: Cmd::from(&cmd),
57 source: err,
58 })
59 })
60 .map_err(From::from);
61
62 Some(check_res)
63 }
64
65 fn run(&self) -> color_eyre::Result<()> {
85 let start = Instant::now();
86
87 self.install().inspect_err(|err| {
89 eprintln!(
90 "{} error installing error=\n{}",
91 self.bin_name().red().bold(),
92 format!("{err:#?}").red()
93 );
94 })?;
95
96 let past_install = Instant::now();
97
98 let mut check_duration = None;
100 let check_start = Instant::now();
101 let check_res = self.check();
102 if check_res.is_some() {
103 check_duration = Some(check_start.elapsed());
104 }
105 match check_res {
106 Some(Ok(check_output)) => {
107 println!(
108 "{} {} check_output=\n{}",
109 self.bin_name().green().bold(),
110 format_timing(start, past_install, check_duration),
111 check_output.trim_matches(|c| c == '\n' || c == '\r')
112 );
113 }
114 Some(Err(err)) => {
115 eprintln!(
116 "{} error checking {} error=\n{}",
117 self.bin_name().red(),
118 format_timing(start, past_install, check_duration),
119 format!("{err:#?}").red()
120 );
121 return Err(err);
122 }
123 None => {
124 println!(
125 "{} {}",
126 self.bin_name().yellow().bold(),
127 format_timing(start, past_install, check_duration),
128 );
129 }
130 }
131
132 Ok(())
133 }
134
135 fn check_args(&self) -> Option<&[&str]> {
137 Some(&["--version"])
138 }
139}
140
141pub trait SystemDependent {
142 fn target_arch_and_os(&self) -> (&str, &str);
143}
144
145fn format_timing(start: Instant, past_install: Instant, check: Option<Duration>) -> String {
153 format!(
154 "install_time={:?} check_time={:?} total_time={:?}",
155 past_install.duration_since(start),
156 check,
157 start.elapsed()
158 )
159}