prettier error reporting

This commit is contained in:
wires 2025-03-22 15:21:01 -04:00
parent 01090ac792
commit d84b074dde
Signed by: wires
SSH key fingerprint: SHA256:9GtP+M3O2IivPDlw1UY872UPUuJH2gI0yG6ExBxaaiM
5 changed files with 142 additions and 17 deletions

13
Cargo.lock generated
View file

@ -85,8 +85,11 @@ checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
name = "chrysopoeia"
version = "0.1.0"
dependencies = [
"anstream",
"anstyle",
"anyhow",
"clap",
"clap-cargo",
"config",
"git2",
"lazy_static",
@ -107,6 +110,16 @@ dependencies = [
"clap_derive",
]
[[package]]
name = "clap-cargo"
version = "0.15.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "d546f0e84ff2bfa4da1ce9b54be42285767ba39c688572ca32412a09a73851e5"
dependencies = [
"anstyle",
"clap",
]
[[package]]
name = "clap_builder"
version = "4.5.32"

View file

@ -5,8 +5,11 @@ edition = "2024"
license = "GPL-3.0-only"
[dependencies]
anstream = "0.6.18"
anstyle = "1.0.10"
anyhow = "1.0.97"
clap = { version = "4.5.32", features = ["derive"] }
clap-cargo = "0.15.2"
config = { version = "0.15.11", default-features = false, features = ["toml"] }
git2 = "0.20.1"
lazy_static = "1.5.0"

View file

@ -11,8 +11,8 @@ pub enum Error {
NoCompiler,
/// user specified a compiler and it didn't work
#[error("couldn't execute `{cmd}`: {src}")]
BadCompiler { cmd: String, src: io::Error },
#[error("couldn't execute `{cmd}`: {e}")]
BadCompiler { cmd: String, e: io::Error },
/// user specified a compiler and it's nonsense
#[error("compiler can't be empty")]
@ -57,7 +57,7 @@ impl<'a> Build<'a> {
return match self.try_compile(cmd, output_path) {
Err(e) => Err(Error::BadCompiler {
cmd: compiler.to_owned(),
src: e,
e,
}),
Ok(output) => to_res(output),
};

70
src/console.rs Normal file
View file

@ -0,0 +1,70 @@
use std::{
fmt::Display,
io::{Write, stderr},
sync::OnceLock,
};
use anstream::AutoStream;
use anstyle::{AnsiColor, Effects, Style};
use clap::ValueEnum;
pub const ERROR: Style = AnsiColor::Red.on_default().effects(Effects::BOLD);
#[derive(Copy, Clone, Debug, PartialEq, Eq, ValueEnum)]
pub enum Color {
Auto,
Always,
Never,
}
impl From<Color> for anstream::ColorChoice {
fn from(value: Color) -> Self {
match value {
Color::Always => Self::Always,
Color::Never => Self::Never,
Color::Auto => Self::Auto,
}
}
}
#[derive(Debug)]
pub struct Console(anstream::ColorChoice);
impl Console {
fn new(color: Color) -> Self {
Self(color.into())
}
pub fn print(&self, status: &str, style: &Style, msg: impl Display) {
let mut stream = AutoStream::new(stderr(), self.0).lock();
let _ = writeln!(stream, "{style}{status}{style:#} {msg}");
}
}
static CONSOLE: OnceLock<Console> = OnceLock::new();
pub fn console() -> &'static Console {
CONSOLE.get().expect("console should be initialized")
}
pub fn init(color: Color) {
CONSOLE
.set(Console::new(color))
.expect("console should only be initialized once")
}
macro_rules! error {
($arg:expr) => (
crate::console::console().print("error:", &crate::console::ERROR, ($arg))
);
($($arg:tt)+) => (
crate::console::console().print(
"error:",
&crate::console::ERROR,
format_args!($($arg)+),
)
);
}
pub(crate) use error;

View file

@ -1,24 +1,43 @@
use std::{env::consts::DLL_EXTENSION, fs::create_dir_all};
use std::{env::consts::DLL_EXTENSION, fs::create_dir_all, path::Path, process::ExitCode};
use clap::Parser;
use clap_cargo::style::CLAP_STYLING;
use git2::Repository;
mod cc;
mod console;
mod languages;
mod settings;
use console::{Color, error};
use languages::{InstallInfo, get_install_info};
use settings::{Settings, get_settings};
#[derive(Debug, Parser)]
#[command(version, about, long_about = None)]
#[command(styles = CLAP_STYLING)]
struct Cli {
/// when to use terminal colors
#[arg(short, long, value_enum, default_value_t = Color::Auto)]
color: Color,
language: String,
}
fn main() -> anyhow::Result<()> {
let Cli { language } = Cli::parse();
fn main() -> ExitCode {
let cli = Cli::parse();
console::init(cli.color);
if let Err(e) = main_inner(cli) {
error!(e);
ExitCode::FAILURE
} else {
ExitCode::SUCCESS
}
}
fn main_inner(Cli { language, .. }: Cli) -> anyhow::Result<()> {
let Settings {
parser_dir,
compiler,
@ -31,23 +50,43 @@ fn main() -> anyhow::Result<()> {
// have to use a canonical path bc compiler runs elsewhere
let parser_dir = parser_dir.canonicalize()?;
let InstallInfo { url, files } =
get_install_info(&language).ok_or(anyhow::anyhow!("unknown language `{language}`"))?;
Ok(install(
&language,
&parser_dir,
src_dir.as_ref(),
compiler.as_deref(),
)?)
}
let repo_path = src_dir.as_ref().join(&language);
Repository::clone(url, &repo_path)?;
#[derive(Debug, thiserror::Error)]
enum InstallError {
#[error("unknown language `{0}`")]
UnknownLanguage(String),
#[error("couldn't clone `{0}`")]
Clone(&'static str),
#[error(transparent)]
Compile(#[from] cc::Error),
}
fn install(
language: &str,
parser_dir: &Path,
src_dir: &Path,
compiler: Option<&str>,
) -> Result<(), InstallError> {
let InstallInfo { url, files } =
get_install_info(language).ok_or(InstallError::UnknownLanguage(language.to_owned()))?;
let repo_path = src_dir.join(language);
Repository::clone(url, &repo_path).map_err(|_| InstallError::Clone(url))?;
let output_path = parser_dir.join(language).with_extension(DLL_EXTENSION);
let mut build = cc::Build::new();
build
.compiler(compiler.as_deref())
.dir(&repo_path)
.input_files(files);
build.compiler(compiler).dir(&repo_path).input_files(files);
build.compile(&output_path)?;
// close explicitly to prevent premature drop
src_dir.close()?;
Ok(())
}