lots of refactoring

This commit is contained in:
wires 2025-03-23 18:57:39 -04:00
parent 2f91a8aa86
commit 19d76c7f17
Signed by: wires
SSH key fingerprint: SHA256:9GtP+M3O2IivPDlw1UY872UPUuJH2gI0yG6ExBxaaiM
5 changed files with 112 additions and 112 deletions

View file

@ -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;

View file

@ -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)

View file

@ -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
})
}

View file

@ -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
View 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)?)?)
}