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 tempfile::tempdir;
|
||||||
|
|
||||||
use crate::console::{GOOD, NOTE, error, fail, status, tip, update, warning};
|
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;
|
mod cc;
|
||||||
pub mod languages;
|
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)]
|
#[derive(Debug, Args)]
|
||||||
pub struct Install {
|
pub struct Install {
|
||||||
|
@ -18,36 +33,22 @@ pub struct Install {
|
||||||
#[arg(short, long)]
|
#[arg(short, long)]
|
||||||
force: bool,
|
force: bool,
|
||||||
|
|
||||||
/// List installable parsers
|
|
||||||
#[arg(short, long)]
|
|
||||||
list: bool,
|
|
||||||
|
|
||||||
languages: Vec<String>,
|
languages: Vec<String>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Install {
|
impl Install {
|
||||||
pub fn run(self, mut settings: Settings) -> anyhow::Result<ExitCode> {
|
pub fn run(self, 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);
|
|
||||||
}
|
|
||||||
|
|
||||||
let src_dir = tempdir()?;
|
let src_dir = tempdir()?;
|
||||||
|
|
||||||
let parser_dir = settings.parser_dir();
|
let parser_dir = settings.parser_dir();
|
||||||
let InstallSettings { compiler } = settings.install;
|
|
||||||
|
|
||||||
create_dir_all(&parser_dir)?;
|
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
|
// have to use a canonical path bc compiler runs elsewhere
|
||||||
let parser_dir = parser_dir.canonicalize()?;
|
let parser_dir = parser_dir.canonicalize()?;
|
||||||
|
|
||||||
|
@ -67,13 +68,13 @@ impl Install {
|
||||||
if !force && is_installed(language, &parser_dir) {
|
if !force && is_installed(language, &parser_dir) {
|
||||||
status!("Skipping", &NOTE, "{language} (already installed)");
|
status!("Skipping", &NOTE, "{language} (already installed)");
|
||||||
skips += 1;
|
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(
|
if let Err(Error::Fatal(_)) = install_with_status(
|
||||||
language,
|
language,
|
||||||
info,
|
info,
|
||||||
&parser_dir,
|
&parser_dir,
|
||||||
src_dir.as_ref(),
|
src_dir.as_ref(),
|
||||||
compiler.as_deref(),
|
settings.install.compiler.as_deref(),
|
||||||
) {
|
) {
|
||||||
return Ok(ExitCode::FAILURE);
|
return Ok(ExitCode::FAILURE);
|
||||||
} else {
|
} 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(
|
fn install_with_status(
|
||||||
language: &str,
|
language: &str,
|
||||||
install_info: &InstallInfo,
|
install_info: &InstallInfo,
|
||||||
|
@ -120,21 +136,6 @@ fn install_with_status(
|
||||||
res
|
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 {
|
impl From<cc::Error> for Error {
|
||||||
fn from(e: cc::Error) -> Self {
|
fn from(e: cc::Error) -> Self {
|
||||||
use cc::Error as E;
|
use cc::Error as E;
|
||||||
|
|
|
@ -13,27 +13,6 @@ pub struct InstallInfo {
|
||||||
|
|
||||||
pub type LanguageDefs = HashMap<String, 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 {
|
pub fn is_installed(language: &str, parser_dir: &Path) -> bool {
|
||||||
parser_dir
|
parser_dir
|
||||||
.join(language)
|
.join(language)
|
||||||
|
|
37
src/main.rs
37
src/main.rs
|
@ -9,10 +9,12 @@ use clap_cargo::style::CLAP_STYLING;
|
||||||
mod console;
|
mod console;
|
||||||
mod install;
|
mod install;
|
||||||
mod settings;
|
mod settings;
|
||||||
|
mod util;
|
||||||
|
|
||||||
use console::{Color, error};
|
use console::{Color, error};
|
||||||
use install::Install;
|
use install::{Install, languages::LanguageDefs};
|
||||||
use settings::{default_config, get_settings};
|
use settings::{Settings, default_config};
|
||||||
|
use util::load_toml;
|
||||||
|
|
||||||
#[derive(Debug, Parser)]
|
#[derive(Debug, Parser)]
|
||||||
#[command(version, about, long_about = None)]
|
#[command(version, about, long_about = None)]
|
||||||
|
@ -34,13 +36,30 @@ struct Cli {
|
||||||
enum Command {
|
enum Command {
|
||||||
/// Install parsers
|
/// Install parsers
|
||||||
Install(Install),
|
Install(Install),
|
||||||
|
|
||||||
|
/// List available parsers
|
||||||
|
Available,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Command {
|
impl Command {
|
||||||
fn run(self, config_path: &Path) -> anyhow::Result<ExitCode> {
|
fn run(self, config_path: &Path) -> anyhow::Result<ExitCode> {
|
||||||
let settings = get_settings(config_path)?;
|
let settings: Settings = load_toml(config_path)?;
|
||||||
|
|
||||||
match self {
|
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),
|
Self::Install(args) => args.run(settings),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -55,10 +74,10 @@ fn main() -> ExitCode {
|
||||||
|
|
||||||
console::init(color);
|
console::init(color);
|
||||||
|
|
||||||
command
|
let config_path = config.unwrap_or_else(default_config);
|
||||||
.run(&config.unwrap_or_else(default_config))
|
|
||||||
.unwrap_or_else(|e| {
|
command.run(&config_path).unwrap_or_else(|e| {
|
||||||
error!("{e}");
|
error!("{e}");
|
||||||
ExitCode::FAILURE
|
ExitCode::FAILURE
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,16 +1,9 @@
|
||||||
use std::{
|
use std::path::PathBuf;
|
||||||
fs::read_to_string,
|
|
||||||
io,
|
|
||||||
path::{Path, PathBuf},
|
|
||||||
};
|
|
||||||
|
|
||||||
use lazy_static::lazy_static;
|
use lazy_static::lazy_static;
|
||||||
use serde::Deserialize;
|
use serde::Deserialize;
|
||||||
|
|
||||||
use crate::{
|
use crate::install::languages::LanguageDefs;
|
||||||
console::warning,
|
|
||||||
install::languages::{LanguageDb, LanguageDefs},
|
|
||||||
};
|
|
||||||
|
|
||||||
lazy_static! {
|
lazy_static! {
|
||||||
static ref XDG: xdg::BaseDirectories =
|
static ref XDG: xdg::BaseDirectories =
|
||||||
|
@ -26,7 +19,8 @@ pub struct Settings {
|
||||||
pub install: InstallSettings,
|
pub install: InstallSettings,
|
||||||
|
|
||||||
#[serde(rename = "languages")]
|
#[serde(rename = "languages")]
|
||||||
pub user_language_defs: Option<LanguageDefs>,
|
#[serde(default)]
|
||||||
|
pub user_defs: LanguageDefs,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Default, Deserialize)]
|
#[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 {
|
impl Settings {
|
||||||
pub fn parser_dir(&self) -> PathBuf {
|
pub fn parser_dir(&self) -> PathBuf {
|
||||||
self.data_dir.join("parsers")
|
self.data_dir.join("parsers")
|
||||||
}
|
}
|
||||||
|
|
||||||
/// note: this method moves out of self.user_language_defs
|
pub fn language_file(&self) -> PathBuf {
|
||||||
pub fn language_defs(&mut self) -> Result<LanguageDb, Error> {
|
self.data_dir.join("languages.toml")
|
||||||
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))
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
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