install multiple + better reporting

This commit is contained in:
wires 2025-03-22 22:21:05 -04:00
parent d84b074dde
commit e9b3f5073f
Signed by: wires
SSH key fingerprint: SHA256:9GtP+M3O2IivPDlw1UY872UPUuJH2gI0yG6ExBxaaiM
6 changed files with 307 additions and 36 deletions

194
Cargo.lock generated
View file

@ -38,7 +38,7 @@ version = "1.1.2"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c" checksum = "79947af37f4177cfead1110013d678905c37501914fba0efea834c3fe9a8d60c"
dependencies = [ dependencies = [
"windows-sys", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -49,7 +49,7 @@ checksum = "ca3534e77181a9cc07539ad51f2141fe32f6c3ffd4df76db8ad92346b003ae4e"
dependencies = [ dependencies = [
"anstyle", "anstyle",
"once_cell", "once_cell",
"windows-sys", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -58,6 +58,12 @@ version = "1.0.97"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f" checksum = "dcfed56ad506cb2c684a14971b8861fdc3baaaae314b9e5f9bb532cbe3ba7a4f"
[[package]]
name = "autocfg"
version = "1.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ace50bade8e6234aa140d9a2f552bbee1db4d353f69b8217bc503490fc1a9f26"
[[package]] [[package]]
name = "bitflags" name = "bitflags"
version = "2.9.0" version = "2.9.0"
@ -91,6 +97,7 @@ dependencies = [
"clap", "clap",
"clap-cargo", "clap-cargo",
"config", "config",
"crossterm",
"git2", "git2",
"lazy_static", "lazy_static",
"phf", "phf",
@ -168,6 +175,31 @@ dependencies = [
"winnow", "winnow",
] ]
[[package]]
name = "crossterm"
version = "0.28.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "829d955a0bb380ef178a640b91779e3987da38c9aea133b20614cfed8cdea9c6"
dependencies = [
"bitflags",
"crossterm_winapi",
"mio",
"parking_lot",
"rustix 0.38.44",
"signal-hook",
"signal-hook-mio",
"winapi",
]
[[package]]
name = "crossterm_winapi"
version = "0.9.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "acdd7c62a3665c7f6830a51635d9ac9b23ed385797f70a83bb8bafe9c572ab2b"
dependencies = [
"winapi",
]
[[package]] [[package]]
name = "displaydoc" name = "displaydoc"
version = "0.2.5" version = "0.2.5"
@ -192,7 +224,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d" checksum = "33d852cb9b869c2a9b3df2f71a3074817f01e1844f839a144f5fcef059a4eb5d"
dependencies = [ dependencies = [
"libc", "libc",
"windows-sys", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -219,7 +251,7 @@ dependencies = [
"cfg-if", "cfg-if",
"libc", "libc",
"r-efi", "r-efi",
"wasi", "wasi 0.14.2+wasi-0.2.4",
] ]
[[package]] [[package]]
@ -465,6 +497,12 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "linux-raw-sys"
version = "0.4.15"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d26c52dbd32dccf2d10cac7725f8eae5296885fb5703b261f7d0a0739ec807ab"
[[package]] [[package]]
name = "linux-raw-sys" name = "linux-raw-sys"
version = "0.9.3" version = "0.9.3"
@ -477,6 +515,16 @@ version = "0.7.5"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856" checksum = "23fb14cb19457329c82206317a5663005a4d404783dc74f4252769b0d5f42856"
[[package]]
name = "lock_api"
version = "0.4.12"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "07af8b9cdd281b7915f413fa73f29ebd5d55d0d3f0155584dade1ff18cea1b17"
dependencies = [
"autocfg",
"scopeguard",
]
[[package]] [[package]]
name = "log" name = "log"
version = "0.4.26" version = "0.4.26"
@ -489,6 +537,18 @@ version = "2.7.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3" checksum = "78ca9ab1a0babb1e7d5695e3530886289c18cf2f87ec19a575a0abdce112e3a3"
[[package]]
name = "mio"
version = "1.0.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "2886843bf800fba2e3377cff24abf6379b4c4d5c6681eaf9ea5b0d15090450bd"
dependencies = [
"libc",
"log",
"wasi 0.11.0+wasi-snapshot-preview1",
"windows-sys 0.52.0",
]
[[package]] [[package]]
name = "once_cell" name = "once_cell"
version = "1.21.1" version = "1.21.1"
@ -513,6 +573,29 @@ dependencies = [
"vcpkg", "vcpkg",
] ]
[[package]]
name = "parking_lot"
version = "0.12.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f1bf18183cf54e8d6059647fc3063646a1801cf30896933ec2311622cc4b9a27"
dependencies = [
"lock_api",
"parking_lot_core",
]
[[package]]
name = "parking_lot_core"
version = "0.9.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "1e401f977ab385c9e4e3ab30627d6f26d00e2c73eef317493c4ec6d468726cf8"
dependencies = [
"cfg-if",
"libc",
"redox_syscall",
"smallvec",
"windows-targets",
]
[[package]] [[package]]
name = "pathdiff" name = "pathdiff"
version = "0.2.3" version = "0.2.3"
@ -612,6 +695,28 @@ version = "0.6.4"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c" checksum = "ec0be4795e2f6a28069bec0b5ff3e2ac9bafc99e6a9a7dc3547996c5c816922c"
[[package]]
name = "redox_syscall"
version = "0.5.10"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0b8c0c260b63a8219631167be35e6a988e9554dbd323f8bd08439c8ed1302bd1"
dependencies = [
"bitflags",
]
[[package]]
name = "rustix"
version = "0.38.44"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "fdb5bc1ae2baa591800df16c9ca78619bf65c0488b41b96ccec5d11220d8c154"
dependencies = [
"bitflags",
"errno",
"libc",
"linux-raw-sys 0.4.15",
"windows-sys 0.59.0",
]
[[package]] [[package]]
name = "rustix" name = "rustix"
version = "1.0.3" version = "1.0.3"
@ -621,10 +726,16 @@ dependencies = [
"bitflags", "bitflags",
"errno", "errno",
"libc", "libc",
"linux-raw-sys", "linux-raw-sys 0.9.3",
"windows-sys", "windows-sys 0.59.0",
] ]
[[package]]
name = "scopeguard"
version = "1.2.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49"
[[package]] [[package]]
name = "serde" name = "serde"
version = "1.0.219" version = "1.0.219"
@ -660,6 +771,36 @@ version = "1.3.0"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64"
[[package]]
name = "signal-hook"
version = "0.3.17"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8621587d4798caf8eb44879d42e56b9a93ea5dcd315a6487c357130095b62801"
dependencies = [
"libc",
"signal-hook-registry",
]
[[package]]
name = "signal-hook-mio"
version = "0.2.4"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "34db1a06d485c9142248b7a054f034b349b212551f3dfd19c94d45a754a217cd"
dependencies = [
"libc",
"mio",
"signal-hook",
]
[[package]]
name = "signal-hook-registry"
version = "1.4.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9e9e0b4211b72e7b8b6e85c807d36c212bdb33ea8587f7569562a84df5465b1"
dependencies = [
"libc",
]
[[package]] [[package]]
name = "siphasher" name = "siphasher"
version = "1.0.1" version = "1.0.1"
@ -715,8 +856,8 @@ dependencies = [
"fastrand", "fastrand",
"getrandom", "getrandom",
"once_cell", "once_cell",
"rustix", "rustix 1.0.3",
"windows-sys", "windows-sys 0.59.0",
] ]
[[package]] [[package]]
@ -824,6 +965,12 @@ version = "0.2.15"
source = "registry+https://github.com/rust-lang/crates.io-index" source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426" checksum = "accd4ea62f7bb7a82fe23066fb0957d48ef677f6eeb8215f372f52e48bb32426"
[[package]]
name = "wasi"
version = "0.11.0+wasi-snapshot-preview1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "9c8d87e72b64a3b4db28d11ce29237c246188f4f51057d65a7eab63b7987e423"
[[package]] [[package]]
name = "wasi" name = "wasi"
version = "0.14.2+wasi-0.2.4" version = "0.14.2+wasi-0.2.4"
@ -833,6 +980,37 @@ dependencies = [
"wit-bindgen-rt", "wit-bindgen-rt",
] ]
[[package]]
name = "winapi"
version = "0.3.9"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "5c839a674fcd7a98952e593242ea400abe93992746761e38641405d28b00f419"
dependencies = [
"winapi-i686-pc-windows-gnu",
"winapi-x86_64-pc-windows-gnu",
]
[[package]]
name = "winapi-i686-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6"
[[package]]
name = "winapi-x86_64-pc-windows-gnu"
version = "0.4.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f"
[[package]]
name = "windows-sys"
version = "0.52.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "282be5f36a8ce781fad8c8ae18fa3f9beff57ec1b52cb3de0789201425d9a33d"
dependencies = [
"windows-targets",
]
[[package]] [[package]]
name = "windows-sys" name = "windows-sys"
version = "0.59.0" version = "0.59.0"

View file

@ -11,6 +11,7 @@ anyhow = "1.0.97"
clap = { version = "4.5.32", features = ["derive"] } clap = { version = "4.5.32", features = ["derive"] }
clap-cargo = "0.15.2" clap-cargo = "0.15.2"
config = { version = "0.15.11", default-features = false, features = ["toml"] } config = { version = "0.15.11", default-features = false, features = ["toml"] }
crossterm = "0.28.1"
git2 = "0.20.1" git2 = "0.20.1"
lazy_static = "1.5.0" lazy_static = "1.5.0"
phf = { version = "0.11.3", features = ["macros"] } phf = { version = "0.11.3", features = ["macros"] }

View file

@ -23,6 +23,15 @@ pub enum Error {
ExecFailed { status: ExitStatus, stderr: Vec<u8> }, ExecFailed { status: ExitStatus, stderr: Vec<u8> },
} }
impl Error {
pub fn fatal(&self) -> bool {
matches!(
self,
Self::NoCompiler | Self::BadCompiler { .. } | Self::EmptyCompiler
)
}
}
fn to_res(Output { status, stderr, .. }: Output) -> Result<(), Error> { fn to_res(Output { status, stderr, .. }: Output) -> Result<(), Error> {
status status
.success() .success()

View file

@ -7,8 +7,10 @@ use std::{
use anstream::AutoStream; use anstream::AutoStream;
use anstyle::{AnsiColor, Effects, Style}; use anstyle::{AnsiColor, Effects, Style};
use clap::ValueEnum; use clap::ValueEnum;
use crossterm::{ExecutableCommand, cursor::MoveToPreviousLine};
pub const ERROR: Style = AnsiColor::Red.on_default().effects(Effects::BOLD); pub const ERROR: Style = AnsiColor::Red.on_default().effects(Effects::BOLD);
pub const GOOD: Style = AnsiColor::Green.on_default().effects(Effects::BOLD);
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)] #[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
pub enum Color { pub enum Color {
@ -36,9 +38,20 @@ impl Console {
} }
pub fn print(&self, status: &str, style: &Style, msg: impl Display) { pub fn print(&self, status: &str, style: &Style, msg: impl Display) {
let mut stream = AutoStream::new(stderr(), self.0).lock(); let mut stream = AutoStream::new(stderr(), self.0);
let _ = writeln!(stream, "{style}{status}{style:#} {msg}"); let _ = writeln!(stream, "{style}{status}{style:#} {msg}");
} }
pub fn print_right(&self, status: &str, style: &Style, msg: impl Display) {
let mut stream = AutoStream::new(stderr(), self.0);
let _ = writeln!(stream, "{style}{status:>12}{style:#} {msg}");
}
pub fn update_right(&self, status: &str, style: &Style, msg: impl Display) {
let mut stream = AutoStream::new(stderr(), self.0).lock();
let _ = stream.execute(MoveToPreviousLine(1));
let _ = writeln!(stream, "{style}{status:>12}{style:#} {msg}");
}
} }
static CONSOLE: OnceLock<Console> = OnceLock::new(); static CONSOLE: OnceLock<Console> = OnceLock::new();
@ -54,10 +67,6 @@ pub fn init(color: Color) {
} }
macro_rules! error { macro_rules! error {
($arg:expr) => (
crate::console::console().print("error:", &crate::console::ERROR, ($arg))
);
($($arg:tt)+) => ( ($($arg:tt)+) => (
crate::console::console().print( crate::console::console().print(
"error:", "error:",
@ -67,4 +76,34 @@ macro_rules! error {
); );
} }
macro_rules! info {
($status:expr, $($arg:tt)+) => (
crate::console::console().print_right(
$status,
&crate::console::GOOD,
format_args!($($arg)+),
)
);
}
macro_rules! fail {
($($arg:tt)+) => {
crate::console::console().update_right("Failed", &crate::console::ERROR, "");
error!($($arg)+);
}
}
macro_rules! update {
($status:expr, $($arg:tt)+) => (
crate::console::console().update_right(
$status,
&crate::console::GOOD,
format_args!($($arg)+),
)
);
}
pub(crate) use error; pub(crate) use error;
pub(crate) use fail;
pub(crate) use info;
pub(crate) use update;

View file

@ -1,6 +1,6 @@
use phf::phf_map; use phf::phf_map;
#[derive(Debug, Clone, Copy)] #[derive(Debug)]
pub struct InstallInfo { pub struct InstallInfo {
pub url: &'static str, pub url: &'static str,
pub files: &'static [&'static str], pub files: &'static [&'static str],
@ -29,6 +29,6 @@ static LANGUAGES: phf::Map<&'static str, InstallInfo> = phf_map! {
}, },
}; };
pub fn get_install_info(language: &str) -> Option<InstallInfo> { pub fn get_install_info(language: &str) -> Option<&InstallInfo> {
LANGUAGES.get(language).copied() LANGUAGES.get(language)
} }

View file

@ -9,7 +9,7 @@ mod console;
mod languages; mod languages;
mod settings; mod settings;
use console::{Color, error}; use console::{Color, error, fail, info, update};
use languages::{InstallInfo, get_install_info}; use languages::{InstallInfo, get_install_info};
use settings::{Settings, get_settings}; use settings::{Settings, get_settings};
@ -21,7 +21,7 @@ struct Cli {
#[arg(short, long, value_enum, default_value_t = Color::Auto)] #[arg(short, long, value_enum, default_value_t = Color::Auto)]
color: Color, color: Color,
language: String, languages: Vec<String>,
} }
fn main() -> ExitCode { fn main() -> ExitCode {
@ -29,15 +29,16 @@ fn main() -> ExitCode {
console::init(cli.color); console::init(cli.color);
if let Err(e) = main_inner(cli) { match install_all(cli.languages) {
error!(e); Err(e) => {
ExitCode::FAILURE error!("{e}");
} else { ExitCode::FAILURE
ExitCode::SUCCESS }
Ok(code) => code,
} }
} }
fn main_inner(Cli { language, .. }: Cli) -> anyhow::Result<()> { fn install_all(mut languages: Vec<String>) -> anyhow::Result<ExitCode> {
let Settings { let Settings {
parser_dir, parser_dir,
compiler, compiler,
@ -50,37 +51,78 @@ fn main_inner(Cli { language, .. }: Cli) -> anyhow::Result<()> {
// have to use a canonical path bc compiler runs elsewhere // have to use a canonical path bc compiler runs elsewhere
let parser_dir = parser_dir.canonicalize()?; let parser_dir = parser_dir.canonicalize()?;
Ok(install( let mut errors = 0;
&language, let mut installed = 0;
&parser_dir,
src_dir.as_ref(), languages.sort();
compiler.as_deref(), languages.dedup();
)?)
for language in &languages {
if let Some(info) = get_install_info(language) {
if let Err(e) = install(
language,
info,
&parser_dir,
src_dir.as_ref(),
compiler.as_deref(),
) {
fail!("{e}");
if e.fatal() {
return Ok(ExitCode::FAILURE);
} else {
errors += 1;
}
} else {
installed += 1;
}
} else {
error!("unknown language `{language}`");
}
}
info!("Finished", "installed {installed} parsers");
Ok(if errors > 0 {
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
})
} }
#[derive(Debug, thiserror::Error)] #[derive(Debug, thiserror::Error)]
enum InstallError { enum InstallError {
#[error("unknown language `{0}`")]
UnknownLanguage(String),
#[error("couldn't clone `{0}`")] #[error("couldn't clone `{0}`")]
Clone(&'static str), Clone(&'static str),
#[error(transparent)]
Git(#[from] git2::Error),
#[error(transparent)] #[error(transparent)]
Compile(#[from] cc::Error), Compile(#[from] cc::Error),
} }
impl InstallError {
fn fatal(&self) -> bool {
match self {
Self::Compile(e) => e.fatal(),
_ => false,
}
}
}
fn install( fn install(
language: &str, language: &str,
InstallInfo { url, files }: &InstallInfo,
parser_dir: &Path, parser_dir: &Path,
src_dir: &Path, src_dir: &Path,
compiler: Option<&str>, compiler: Option<&str>,
) -> Result<(), InstallError> { ) -> Result<(), InstallError> {
let InstallInfo { url, files } = info!("Installing", "{language}");
get_install_info(language).ok_or(InstallError::UnknownLanguage(language.to_owned()))?;
let repo_path = src_dir.join(language); let repo_path = src_dir.join(language);
Repository::clone(url, &repo_path).map_err(|_| InstallError::Clone(url))?; let repo = Repository::clone(url, &repo_path).map_err(|_| InstallError::Clone(url))?;
let version = repo.head()?.peel_to_commit()?.id();
let output_path = parser_dir.join(language).with_extension(DLL_EXTENSION); let output_path = parser_dir.join(language).with_extension(DLL_EXTENSION);
@ -88,5 +130,7 @@ fn install(
build.compiler(compiler).dir(&repo_path).input_files(files); build.compiler(compiler).dir(&repo_path).input_files(files);
build.compile(&output_path)?; build.compile(&output_path)?;
update!("Installed", "{language} {version:.7}");
Ok(()) Ok(())
} }