lots of refactoring
This commit is contained in:
parent
2f91a8aa86
commit
19d76c7f17
5 changed files with 112 additions and 112 deletions
|
@ -5,12 +5,27 @@ use git2::Repository;
|
|||
use tempfile::tempdir;
|
||||
|
||||
use crate::console::{GOOD, NOTE, error, fail, status, tip, update, warning};
|
||||
use crate::settings::{InstallSettings, Settings};
|
||||
use crate::settings::Settings;
|
||||
use crate::util::load_toml;
|
||||
|
||||
mod cc;
|
||||
pub mod languages;
|
||||
mod lock;
|
||||
|
||||
use languages::{InstallInfo, Revision, is_installed};
|
||||
use languages::{InstallInfo, LanguageDefs, Revision, is_installed};
|
||||
|
||||
struct LanguageDb {
|
||||
user: LanguageDefs,
|
||||
default: LanguageDefs,
|
||||
}
|
||||
|
||||
impl LanguageDb {
|
||||
pub fn get(&self, language: &str) -> Option<&InstallInfo> {
|
||||
self.user
|
||||
.get(language)
|
||||
.or_else(|| self.default.get(language))
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Args)]
|
||||
pub struct Install {
|
||||
|
@ -18,36 +33,22 @@ pub struct Install {
|
|||
#[arg(short, long)]
|
||||
force: bool,
|
||||
|
||||
/// List installable parsers
|
||||
#[arg(short, long)]
|
||||
list: bool,
|
||||
|
||||
languages: Vec<String>,
|
||||
}
|
||||
|
||||
impl Install {
|
||||
pub fn run(self, mut settings: Settings) -> anyhow::Result<ExitCode> {
|
||||
let language_defs = settings.language_defs()?;
|
||||
|
||||
if self.list {
|
||||
let mut available = language_defs.into_keys().collect::<Vec<_>>();
|
||||
available.sort();
|
||||
available.dedup();
|
||||
|
||||
for language in available {
|
||||
println!("{language}");
|
||||
}
|
||||
|
||||
return Ok(ExitCode::SUCCESS);
|
||||
}
|
||||
|
||||
pub fn run(self, settings: Settings) -> anyhow::Result<ExitCode> {
|
||||
let src_dir = tempdir()?;
|
||||
|
||||
let parser_dir = settings.parser_dir();
|
||||
let InstallSettings { compiler } = settings.install;
|
||||
|
||||
create_dir_all(&parser_dir)?;
|
||||
|
||||
let db = LanguageDb {
|
||||
default: load_toml(&settings.language_file())?,
|
||||
user: settings.user_defs,
|
||||
};
|
||||
|
||||
// have to use a canonical path bc compiler runs elsewhere
|
||||
let parser_dir = parser_dir.canonicalize()?;
|
||||
|
||||
|
@ -67,13 +68,13 @@ impl Install {
|
|||
if !force && is_installed(language, &parser_dir) {
|
||||
status!("Skipping", &NOTE, "{language} (already installed)");
|
||||
skips += 1;
|
||||
} else if let Some(info) = language_defs.get(language) {
|
||||
} else if let Some(info) = db.get(language) {
|
||||
if let Err(Error::Fatal(_)) = install_with_status(
|
||||
language,
|
||||
info,
|
||||
&parser_dir,
|
||||
src_dir.as_ref(),
|
||||
compiler.as_deref(),
|
||||
settings.install.compiler.as_deref(),
|
||||
) {
|
||||
return Ok(ExitCode::FAILURE);
|
||||
} else {
|
||||
|
@ -97,6 +98,21 @@ impl Install {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error("couldn't clone `{0}`")]
|
||||
Clone(String),
|
||||
|
||||
#[error(transparent)]
|
||||
Git(#[from] git2::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Compile(cc::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Fatal(cc::Error),
|
||||
}
|
||||
|
||||
fn install_with_status(
|
||||
language: &str,
|
||||
install_info: &InstallInfo,
|
||||
|
@ -120,21 +136,6 @@ fn install_with_status(
|
|||
res
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum Error {
|
||||
#[error("couldn't clone `{0}`")]
|
||||
Clone(String),
|
||||
|
||||
#[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;
|
||||
|
|
|
@ -13,27 +13,6 @@ pub struct InstallInfo {
|
|||
|
||||
pub type LanguageDefs = HashMap<String, InstallInfo>;
|
||||
|
||||
pub struct LanguageDb(Vec<LanguageDefs>);
|
||||
|
||||
impl LanguageDb {
|
||||
pub fn new(sources: Vec<HashMap<String, InstallInfo>>) -> Self {
|
||||
Self(sources)
|
||||
}
|
||||
|
||||
pub fn get(&self, language: &str) -> Option<&InstallInfo> {
|
||||
for source in &self.0 {
|
||||
if let Some(info) = source.get(language) {
|
||||
return Some(info);
|
||||
}
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
pub fn into_keys(self) -> impl Iterator<Item = String> {
|
||||
self.0.into_iter().flat_map(HashMap::into_keys)
|
||||
}
|
||||
}
|
||||
|
||||
pub fn is_installed(language: &str, parser_dir: &Path) -> bool {
|
||||
parser_dir
|
||||
.join(language)
|
||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -9,10 +9,12 @@ use clap_cargo::style::CLAP_STYLING;
|
|||
mod console;
|
||||
mod install;
|
||||
mod settings;
|
||||
mod util;
|
||||
|
||||
use console::{Color, error};
|
||||
use install::Install;
|
||||
use settings::{default_config, get_settings};
|
||||
use install::{Install, languages::LanguageDefs};
|
||||
use settings::{Settings, default_config};
|
||||
use util::load_toml;
|
||||
|
||||
#[derive(Debug, Parser)]
|
||||
#[command(version, about, long_about = None)]
|
||||
|
@ -34,13 +36,30 @@ struct Cli {
|
|||
enum Command {
|
||||
/// Install parsers
|
||||
Install(Install),
|
||||
|
||||
/// List available parsers
|
||||
Available,
|
||||
}
|
||||
|
||||
impl Command {
|
||||
fn run(self, config_path: &Path) -> anyhow::Result<ExitCode> {
|
||||
let settings = get_settings(config_path)?;
|
||||
let settings: Settings = load_toml(config_path)?;
|
||||
|
||||
match self {
|
||||
Self::Available => {
|
||||
let default_defs: LanguageDefs = load_toml(&settings.language_file())?;
|
||||
|
||||
let mut languages = settings.user_defs.into_keys().collect::<Vec<_>>();
|
||||
languages.extend(default_defs.into_keys());
|
||||
languages.sort();
|
||||
languages.dedup();
|
||||
|
||||
for language in languages {
|
||||
println!("{language}");
|
||||
}
|
||||
|
||||
Ok(ExitCode::SUCCESS)
|
||||
}
|
||||
Self::Install(args) => args.run(settings),
|
||||
}
|
||||
}
|
||||
|
@ -55,10 +74,10 @@ fn main() -> ExitCode {
|
|||
|
||||
console::init(color);
|
||||
|
||||
command
|
||||
.run(&config.unwrap_or_else(default_config))
|
||||
.unwrap_or_else(|e| {
|
||||
error!("{e}");
|
||||
ExitCode::FAILURE
|
||||
})
|
||||
let config_path = config.unwrap_or_else(default_config);
|
||||
|
||||
command.run(&config_path).unwrap_or_else(|e| {
|
||||
error!("{e}");
|
||||
ExitCode::FAILURE
|
||||
})
|
||||
}
|
||||
|
|
|
@ -1,16 +1,9 @@
|
|||
use std::{
|
||||
fs::read_to_string,
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
use std::path::PathBuf;
|
||||
|
||||
use lazy_static::lazy_static;
|
||||
use serde::Deserialize;
|
||||
|
||||
use crate::{
|
||||
console::warning,
|
||||
install::languages::{LanguageDb, LanguageDefs},
|
||||
};
|
||||
use crate::install::languages::LanguageDefs;
|
||||
|
||||
lazy_static! {
|
||||
static ref XDG: xdg::BaseDirectories =
|
||||
|
@ -26,7 +19,8 @@ pub struct Settings {
|
|||
pub install: InstallSettings,
|
||||
|
||||
#[serde(rename = "languages")]
|
||||
pub user_language_defs: Option<LanguageDefs>,
|
||||
#[serde(default)]
|
||||
pub user_defs: LanguageDefs,
|
||||
}
|
||||
|
||||
#[derive(Debug, Default, Deserialize)]
|
||||
|
@ -51,43 +45,12 @@ pub fn default_config() -> PathBuf {
|
|||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
pub enum Error {
|
||||
#[error("unable to read config file: {0}")]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error("{file:?}: {e}")]
|
||||
Toml { file: PathBuf, e: toml::de::Error },
|
||||
}
|
||||
|
||||
pub fn get_settings(path: &Path) -> Result<Settings, Error> {
|
||||
toml::from_str(&read_to_string(path)?).map_err(|e| Error::Toml {
|
||||
file: path.to_owned(),
|
||||
e,
|
||||
})
|
||||
}
|
||||
|
||||
impl Settings {
|
||||
pub fn parser_dir(&self) -> PathBuf {
|
||||
self.data_dir.join("parsers")
|
||||
}
|
||||
|
||||
/// note: this method moves out of self.user_language_defs
|
||||
pub fn language_defs(&mut self) -> Result<LanguageDb, Error> {
|
||||
let mut sources: Vec<LanguageDefs> = self.user_language_defs.take().into_iter().collect();
|
||||
|
||||
let default_path = self.data_dir.join("languages.toml");
|
||||
|
||||
match read_to_string(&default_path) {
|
||||
Ok(default_defs) => {
|
||||
sources.push(toml::from_str(&default_defs).map_err(|e| Error::Toml {
|
||||
file: default_path,
|
||||
e,
|
||||
})?)
|
||||
}
|
||||
Err(e) => warning!("couldn't read language install info from {default_path:?}: {e}"),
|
||||
}
|
||||
|
||||
Ok(LanguageDb::new(sources))
|
||||
pub fn language_file(&self) -> PathBuf {
|
||||
self.data_dir.join("languages.toml")
|
||||
}
|
||||
}
|
||||
|
|
38
src/util.rs
Normal file
38
src/util.rs
Normal file
|
@ -0,0 +1,38 @@
|
|||
use std::{
|
||||
fs::read_to_string,
|
||||
io,
|
||||
path::{Path, PathBuf},
|
||||
};
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
#[error("{path:?}: {e}")]
|
||||
pub struct TomlError {
|
||||
path: PathBuf,
|
||||
e: TomlErrorInner,
|
||||
}
|
||||
|
||||
#[derive(Debug, thiserror::Error)]
|
||||
enum TomlErrorInner {
|
||||
#[error(transparent)]
|
||||
Io(#[from] io::Error),
|
||||
|
||||
#[error(transparent)]
|
||||
Deserialize(#[from] toml::de::Error),
|
||||
}
|
||||
|
||||
pub fn load_toml<T>(path: &Path) -> Result<T, TomlError>
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
load_toml_inner(path).map_err(|e| TomlError {
|
||||
path: path.to_owned(),
|
||||
e,
|
||||
})
|
||||
}
|
||||
|
||||
fn load_toml_inner<T>(path: &Path) -> Result<T, TomlErrorInner>
|
||||
where
|
||||
T: serde::de::DeserializeOwned,
|
||||
{
|
||||
Ok(toml::from_str(&read_to_string(path)?)?)
|
||||
}
|
Loading…
Add table
Reference in a new issue