#![feature(once_cell, never_type, exit_status_error, async_closure, let_chains)]
mod completion_line;
mod completions;
mod config;
mod dir_walker;
mod nu;
mod patching;
use beau_collector::BeauCollector as _;
use patching::fetch_latest_patch_set;
use std::{
fs::{create_dir, File},
io::{BufRead, BufReader, Seek, Write},
path::{Path, PathBuf},
sync::LazyLock,
};
use config::Config;
use log::{debug, info, trace};
use crate::nu::{processing_failed, CompletionsProcessor};
#[tokio::main]
async fn main() -> anyhow::Result<()> {
femme::with_level(Config::verbose().log_level_filter());
let mut conversion_errors: Vec<Result<_, _>> = vec![];
if let Some(options) = Config::generate_patches() {
patching::generate_patches(options)?;
} else if let Some(install_location) = Config::install() {
install_config(install_location)?;
} else {
if Config::update_patch_set() {
fetch_latest_patch_set().await?;
}
if Config::convert() {
if !Config::output_dir().exists() {
trace!(
"output directory '{:?}' does not exist, creating",
Config::output_dir()
);
create_dir(Config::output_dir())?;
debug!("created output directory {:?}", Config::output_dir());
}
let processor = CompletionsProcessor::default();
info!("beginning translation phase");
for source in Config::sources() {
let path: PathBuf = source.into();
if let Err(err) = processor.process_file_or_dir(path) {
let result = processing_failed(source, err).map(|_| unreachable!());
if Config::fail_fast() {
trace!("failing fast");
return result;
} else {
trace!("deferring failure");
conversion_errors.push(result);
}
}
}
debug!(error_count = conversion_errors.len(); "finished processing all translations");
processor.write_sourcing_file(&Config::imports_location())?;
info!("finished translation phase");
}
if Config::patch() {
info!("beginning patch phase");
patching::patch_all()?;
info!("finished patching");
}
conversion_errors.into_iter().bcollect::<Vec<()>>()?;
}
Ok(())
}
fn install_config(location: &Path) -> anyhow::Result<()> {
static CONFIG_DEF: LazyLock<String> =
LazyLock::new(|| format!("source {:?}", Config::imports_location()));
let mut file = File::options().read(true).write(true).open(location)?;
file.seek(std::io::SeekFrom::Start(0))?;
let buf_reader = BufReader::new(&file);
for (n, line) in buf_reader.lines().enumerate() {
let line = line?;
if line.contains(&*CONFIG_DEF) {
info!(line_number = n; "config found");
return Ok(());
}
}
file.seek(std::io::SeekFrom::End(0))?;
file.write_all(b"\n")?;
file.write_all(CONFIG_DEF.as_bytes())?;
file.write_all(b"\n")?;
debug!("config written");
Ok(())
}