#![allow(clippy::single_component_path_imports)]
mod cli;
mod config;
mod filesystem;
mod lock;
mod table;
use std::path::Path;
use anyhow::Result;
use clap::Parser;
use crate::cli::Cli;
use crate::cli::{AddArgs, Command, RemoveArgs, StatusArgs, SyncArgs};
use crate::filesystem::AbsPath;
use crate::lock::Lock;
use crate::table::Table;
fn resolve_path_to_file(path: &Path, file_name: &str) -> Result<AbsPath> {
let mut path = path.canonicalize()?;
if path.is_dir() {
path.push(file_name);
}
Ok(AbsPath::from_owned_path(path)?)
}
fn load_table_and_lock(config_path: &Path) -> Result<(Table, Lock)> {
let symp_toml_file = resolve_path_to_file(config_path, "symp.toml")?;
let symp_lock_file = AbsPath::from_owned_path(
symp_toml_file
.parent()
.expect("parent path should exist")
.join({
let mut file_name = symp_toml_file
.file_stem()
.expect("is only None if path terminates in ..")
.to_os_string();
file_name.push(".lock");
file_name
}),
)?;
let table = Table::load(&symp_toml_file)?;
let lock = Lock::load(&symp_lock_file)?;
Ok((table, lock))
}
fn load_lock(config_path: &Path) -> Result<Lock> {
let symp_toml_file = resolve_path_to_file(config_path, "symp.toml")?;
let symp_lock_file = AbsPath::from_owned_path(
symp_toml_file
.parent()
.expect("parent path should exist")
.join({
let mut file_name = symp_toml_file
.file_stem()
.expect("is only None if path terminates in ..")
.to_os_string();
file_name.push(".lock");
file_name
}),
)?;
let lock = Lock::load(&symp_lock_file)?;
Ok(lock)
}
fn add(args: AddArgs) -> Result<()> {
let (table, mut lock) = load_table_and_lock(&args.config)?;
if args.all || (args.packages.is_empty() && args.profile.is_empty()) {
lock.mark_for_removal(lock.all_package_names().iter());
lock.add(table.links.iter());
table.validate_table_packages(table.packages.iter().map(|pkg| &pkg.package_name))?;
} else {
let mut package_names = Vec::new();
package_names.extend(args.packages.iter().cloned());
for profile in args.profile.iter() {
package_names.extend(table.get_package_names_from_profile(profile)?);
}
package_names.sort();
package_names.dedup();
if !package_names.is_empty() {
table.check_packages_exist(package_names.iter())?;
let dropped_package_names: Vec<_> = lock
.links
.keys()
.filter_map(|name| {
match !table.packages.iter().any(|pkg| pkg.package_name.eq(name)) {
true => Some(name.to_string()),
false => None,
}
})
.collect();
lock.mark_for_removal(dropped_package_names.iter());
lock.mark_for_removal(package_names.iter());
lock.add(
table
.links
.iter()
.filter(|link| package_names.contains(&link.package_data.package_name)),
);
table.validate_table_packages(package_names.iter())?;
}
}
if !args.no_sync {
lock.sync()?;
}
lock.save()?;
Ok(())
}
fn remove(args: RemoveArgs) -> Result<()> {
let (table, mut lock) = load_table_and_lock(&args.config)?;
if args.all || (args.packages.is_empty() && args.profile.is_empty()) {
lock.mark_for_removal(lock.all_package_names().iter());
table.validate_table_packages(table.packages.iter().map(|pkg| &pkg.package_name))?;
} else {
let mut package_names = Vec::new();
package_names.extend(args.packages.iter().cloned());
for profile in args.profile.iter() {
package_names.extend(table.get_package_names_from_profile(profile)?);
}
package_names.sort();
package_names.dedup();
if !package_names.is_empty() {
table.check_packages_exist(package_names.iter())?;
let dropped_package_names: Vec<_> = lock
.links
.keys()
.filter_map(|name| {
match !table.packages.iter().any(|pkg| pkg.package_name.eq(name)) {
true => Some(name.to_string()),
false => None,
}
})
.collect();
lock.mark_for_removal(dropped_package_names.iter());
lock.mark_for_removal(package_names.iter());
table.validate_table_packages(package_names.iter())?;
}
}
if !args.no_sync {
lock.sync()?;
}
lock.save()?;
Ok(())
}
fn sync(args: SyncArgs) -> Result<()> {
let mut lock = load_lock(&args.config)?;
lock.sync()?;
lock.save()?;
Ok(())
}
fn status(args: StatusArgs) -> Result<()> {
let (table, lock) = load_table_and_lock(&args.config)?;
if args.all || (args.packages.is_empty() && args.profile.is_empty()) {
table.validate_table_packages(table.packages.iter().map(|pkg| &pkg.package_name))?;
table.print_status(table.packages.iter().map(|pkg| &pkg.package_name), &lock)?;
} else {
let mut package_names = Vec::new();
package_names.extend(args.packages.iter().cloned());
for profile in args.profile.iter() {
package_names.extend(table.get_package_names_from_profile(profile)?);
}
package_names.sort();
package_names.dedup();
if !package_names.is_empty() {
table.check_packages_exist(package_names.iter())?;
table.validate_table_packages(package_names.iter())?;
table.print_status(package_names.iter(), &lock)?;
}
}
Ok(())
}
fn blahaj() {
println!("🦈 says:\n\n🏳️⚧️ Trans Rights are Human Rights! 🏳️⚧️");
}
fn simp() {
println!("https://www.youtube.com/watch?v=vYVhUvNINTA");
}
fn main() -> Result<()> {
let cli = Cli::parse();
#[cfg(debug_assertions)]
println!("{:#?}", cli);
match cli.command {
Command::Add(args) => add(args)?,
Command::Remove(args) => remove(args)?,
Command::Sync(args) => sync(args)?,
Command::Status(args) => status(args)?,
Command::Blahaj => blahaj(),
Command::Simp => simp(),
};
Ok(())
}