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 {
|
macro_rules! fail {
|
||||||
($($arg:tt)+) => {
|
($($arg:tt)+) => {
|
||||||
crate::console::console().update_right("Failed", &crate::console::ERROR, "");
|
{
|
||||||
error!($($arg)+);
|
crate::console::console().update_right("Failed", &crate::console::ERROR, "");
|
||||||
|
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> },
|
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()
|
|
@ -1,5 +1,7 @@
|
||||||
use phf::phf_map;
|
use phf::phf_map;
|
||||||
|
|
||||||
|
pub(super) type Revision = git2::Oid;
|
||||||
|
|
||||||
#[derive(Debug)]
|
#[derive(Debug)]
|
||||||
pub struct InstallInfo {
|
pub struct InstallInfo {
|
||||||
pub url: &'static str,
|
pub url: &'static str,
|
140
src/main.rs
140
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 clap_cargo::style::CLAP_STYLING;
|
||||||
use git2::Repository;
|
|
||||||
|
|
||||||
mod cc;
|
|
||||||
mod console;
|
mod console;
|
||||||
mod languages;
|
mod install;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
|
||||||
use console::{Color, error, fail, info, update};
|
use console::{Color, error};
|
||||||
use languages::{InstallInfo, get_install_info};
|
use install::Install;
|
||||||
use settings::{Settings, get_settings};
|
use settings::get_settings;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
|
@ -21,116 +19,32 @@ 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,
|
||||||
|
|
||||||
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 {
|
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) {
|
command.run().unwrap_or_else(|e| {
|
||||||
Err(e) => {
|
error!("{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
|
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