reorganization, add subcommand
This commit is contained in:
parent
e9b3f5073f
commit
2b513603b7
5 changed files with 166 additions and 124 deletions
|
@ -88,8 +88,10 @@ macro_rules! info {
|
|||
|
||||
macro_rules! fail {
|
||||
($($arg:tt)+) => {
|
||||
{
|
||||
crate::console::console().update_right("Failed", &crate::console::ERROR, "");
|
||||
error!($($arg)+);
|
||||
error!($($arg)+)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
133
src/install.rs
Normal file
133
src/install.rs
Normal file
|
@ -0,0 +1,133 @@
|
|||
use std::{env::consts::DLL_EXTENSION, fs::create_dir_all, path::Path, process::ExitCode};
|
||||
|
||||
use clap::Args;
|
||||
use git2::Repository;
|
||||
use tempfile::tempdir;
|
||||
|
||||
use crate::console::{error, fail, info, update};
|
||||
use crate::settings::Settings;
|
||||
|
||||
mod cc;
|
||||
mod languages;
|
||||
|
||||
use languages::{InstallInfo, get_install_info};
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Install {
|
||||
languages: Vec<String>,
|
||||
}
|
||||
|
||||
impl Install {
|
||||
pub fn run(
|
||||
self,
|
||||
Settings {
|
||||
parser_dir,
|
||||
compiler,
|
||||
}: Settings,
|
||||
) -> anyhow::Result<ExitCode> {
|
||||
let src_dir = tempdir()?;
|
||||
|
||||
create_dir_all(&parser_dir)?;
|
||||
|
||||
// have to use a canonical path bc compiler runs elsewhere
|
||||
let parser_dir = parser_dir.canonicalize()?;
|
||||
|
||||
let mut languages = self.languages;
|
||||
|
||||
languages.sort();
|
||||
languages.dedup();
|
||||
|
||||
let mut errors = 0;
|
||||
|
||||
for language in &languages {
|
||||
if let Some(info) = get_install_info(language) {
|
||||
if let Err(Error::Fatal(_)) = install_with_status(
|
||||
language,
|
||||
info,
|
||||
&parser_dir,
|
||||
src_dir.as_ref(),
|
||||
compiler.as_deref(),
|
||||
) {
|
||||
return Ok(ExitCode::FAILURE);
|
||||
} else {
|
||||
errors += 1;
|
||||
}
|
||||
} else {
|
||||
error!("unknown language {language}");
|
||||
errors += 1;
|
||||
}
|
||||
}
|
||||
|
||||
Ok(if errors > 0 {
|
||||
ExitCode::FAILURE
|
||||
} else {
|
||||
ExitCode::SUCCESS
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
fn install_with_status(
|
||||
language: &str,
|
||||
install_info: &InstallInfo,
|
||||
parser_dir: &Path,
|
||||
src_dir: &Path,
|
||||
compiler: Option<&str>,
|
||||
) -> Result<languages::Revision, Error> {
|
||||
info!("Installing", "{language}");
|
||||
|
||||
let res = install(language, install_info, parser_dir, src_dir, compiler);
|
||||
|
||||
match &res {
|
||||
Err(e) => fail!("{e}"),
|
||||
Ok(revision) => update!("Installed", "{language} {revision:.7}"),
|
||||
}
|
||||
|
||||
res
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error("couldn't clone `{0}`")]
|
||||
Clone(&'static str),
|
||||
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Compile(cc::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Fatal(cc::Error),
|
||||
}
|
||||
|
||||
impl From<cc::Error> for Error {
|
||||
fn from(e: cc::Error) -> Self {
|
||||
use cc::Error as E;
|
||||
|
||||
match e {
|
||||
E::NoCompiler | E::BadCompiler { .. } | E::EmptyCompiler => Self::Fatal(e),
|
||||
_ => Self::Compile(e),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn install(
|
||||
language: &str,
|
||||
InstallInfo { url, files }: &InstallInfo,
|
||||
parser_dir: &Path,
|
||||
src_dir: &Path,
|
||||
compiler: Option<&str>,
|
||||
) -> Result<git2::Oid, Error> {
|
||||
let repo_path = src_dir.join(language);
|
||||
let repo = Repository::clone(url, &repo_path).map_err(|_| Error::Clone(url))?;
|
||||
|
||||
let version = repo.head()?.peel_to_commit()?.id();
|
||||
|
||||
let output_path = parser_dir.join(language).with_extension(DLL_EXTENSION);
|
||||
|
||||
let mut build = cc::Build::new();
|
||||
build.compiler(compiler).dir(&repo_path).input_files(files);
|
||||
build.compile(&output_path)?;
|
||||
|
||||
Ok(version)
|
||||
}
|
|
@ -23,15 +23,6 @@ pub enum Error {
|
|||
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> {
|
||||
status
|
||||
.success()
|
|
@ -1,5 +1,7 @@
|
|||
use phf::phf_map;
|
||||
|
||||
pub(super) type Revision = git2::Oid;
|
||||
|
||||
#[derive(Debug)]
|
||||
pub struct InstallInfo {
|
||||
pub url: &'static str,
|
138
src/main.rs
138
src/main.rs
|
@ -1,17 +1,15 @@
|
|||
use std::{env::consts::DLL_EXTENSION, fs::create_dir_all, path::Path, process::ExitCode};
|
||||
use std::process::ExitCode;
|
||||
|
||||
use clap::Parser;
|
||||
use clap::{Parser, Subcommand};
|
||||
use clap_cargo::style::CLAP_STYLING;
|
||||
use git2::Repository;
|
||||
|
||||
mod cc;
|
||||
mod console;
|
||||
mod languages;
|
||||
mod install;
|
||||
mod settings;
|
||||
|
||||
use console::{Color, error, fail, info, update};
|
||||
use languages::{InstallInfo, get_install_info};
|
||||
use settings::{Settings, get_settings};
|
||||
use console::{Color, error};
|
||||
use install::Install;
|
||||
use settings::get_settings;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
|
@ -21,116 +19,32 @@ struct Cli {
|
|||
#[arg(short, long, value_enum, default_value_t = Color::Auto)]
|
||||
color: Color,
|
||||
|
||||
languages: Vec<String>,
|
||||
#[command(subcommand)]
|
||||
command: Command,
|
||||
}
|
||||
|
||||
#[derive(Debug, Subcommand)]
|
||||
enum Command {
|
||||
Install(Install),
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn run(self) -> anyhow::Result<ExitCode> {
|
||||
let settings = get_settings()?;
|
||||
|
||||
match self {
|
||||
Self::Install(args) => args.run(settings),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() -> ExitCode {
|
||||
let cli = Cli::parse();
|
||||
let Cli { color, command } = Cli::parse();
|
||||
|
||||
console::init(cli.color);
|
||||
console::init(color);
|
||||
|
||||
match install_all(cli.languages) {
|
||||
Err(e) => {
|
||||
command.run().unwrap_or_else(|e| {
|
||||
error!("{e}");
|
||||
ExitCode::FAILURE
|
||||
}
|
||||
Ok(code) => code,
|
||||
}
|
||||
}
|
||||
|
||||
fn install_all(mut languages: Vec<String>) -> anyhow::Result<ExitCode> {
|
||||
let Settings {
|
||||
parser_dir,
|
||||
compiler,
|
||||
} = get_settings()?;
|
||||
|
||||
let src_dir = tempfile::tempdir()?;
|
||||
|
||||
create_dir_all(&parser_dir)?;
|
||||
|
||||
// have to use a canonical path bc compiler runs elsewhere
|
||||
let parser_dir = parser_dir.canonicalize()?;
|
||||
|
||||
let mut errors = 0;
|
||||
let mut installed = 0;
|
||||
|
||||
languages.sort();
|
||||
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)]
|
||||
enum InstallError {
|
||||
#[error("couldn't clone `{0}`")]
|
||||
Clone(&'static str),
|
||||
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Compile(#[from] cc::Error),
|
||||
}
|
||||
|
||||
impl InstallError {
|
||||
fn fatal(&self) -> bool {
|
||||
match self {
|
||||
Self::Compile(e) => e.fatal(),
|
||||
_ => false,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn install(
|
||||
language: &str,
|
||||
InstallInfo { url, files }: &InstallInfo,
|
||||
parser_dir: &Path,
|
||||
src_dir: &Path,
|
||||
compiler: Option<&str>,
|
||||
) -> Result<(), InstallError> {
|
||||
info!("Installing", "{language}");
|
||||
|
||||
let repo_path = src_dir.join(language);
|
||||
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 mut build = cc::Build::new();
|
||||
build.compiler(compiler).dir(&repo_path).input_files(files);
|
||||
build.compile(&output_path)?;
|
||||
|
||||
update!("Installed", "{language} {version:.7}");
|
||||
|
||||
Ok(())
|
||||
}
|
||||
|
|
Loading…
Add table
Reference in a new issue