// nixos-config - My current NixOS configuration // // Copyright (C) 2025 Benedikt Peetz // SPDX-License-Identifier: GPL-3.0-or-later // // This file is part of my nixos-config. // // You should have received a copy of the License along with this program. // If not, see . use std::path::{Path, PathBuf}; use anyhow::{Context, Result}; use clap::Parser; use cli::{Args, Command}; use log::trace; use mapping::map_key::MapKey; use walkdir::{DirEntry, WalkDir}; use crate::mapping::MappingsTrie; mod cli; mod mapping; fn main() -> anyhow::Result<()> { let args = Args::parse(); stderrlog::new() .module(module_path!()) .quiet(args.quiet) .show_module_names(false) .color(stderrlog::ColorChoice::Auto) .verbosity(args.verbosity as usize) .timestamp(stderrlog::Timestamp::Off) .init()?; let mut mappings = MappingsTrie::new(); let relevant_directories = match &args.command { Command::Visualize { options } => &options.relevant_directories, Command::Generate { options } => &options.relevant_directories, Command::Interactive { options } => &options.relevant_directories, }; for dir in relevant_directories { trace!("START Processing '{}'..", dir.display()); let path = strip_path(dir, &args.home_name)?; mappings .include(path_to_str(path)?) .with_context(|| format!("Failed to include path: '{}'", path.display()))?; trace!("END Finished processing {}.", dir.display()); } trace!("Generated mappings for the relevant directories. Starting expanding to max depth."); if log::log_enabled!(log::Level::Trace) { eprintln!("{mappings}"); } let home = path_to_str(&args.home_name)?.to_owned(); let mut current_depth = 1; while current_depth != args.depth { for (keys, child) in mappings.0.iter().filter(|(_, child)| child.expendable) { trace!("Adding to child '{}' ('{}')", MapKey::display(&keys), child); let mut local_mappings = MappingsTrie::new(); for dir in WalkDir::new(extend(&home, &child.path)?) .min_depth(1) .max_depth(1) .into_iter() .filter_entry(|e| is_dir(e) && !is_hidden(e)) { let directory = dir.with_context(|| { format!("Failed to read dir ('{}')", home.clone() + &child.path) })?; let path_to_strip = &PathBuf::from(extend(&home, &child.path)?); let path = strip_path(directory.path(), path_to_strip)?; trace!( "Including: '{}' (after stripping '{}' from '{}')", path.display(), path_to_strip.display(), directory.path().display(), ); let gen_key = MapKey::new_ones_from_path(path_to_str(path)?, 1); local_mappings .insert( &gen_key, path_to_str(strip_path(directory.path(), &PathBuf::from(&home))?)?, ) .with_context(|| format!("Failed to include path: '{}'", path.display()))?; } mappings.add_trie(&keys, local_mappings)?; } current_depth += 1; } match args.command { Command::Visualize { .. } => println!("{}", mappings.0), Command::Generate { .. } => println!("{}", mappings.to_lf_mappings(args.home_name)), Command::Interactive { .. } => mappings.interactive_start(args.home_name)?, } Ok(()) } fn extend(base: &str, value: &str) -> Result { let base_path = PathBuf::from(base); let value_path = PathBuf::from(value); Ok(path_to_str(&base_path.join(&value_path))?.to_owned()) } fn is_hidden(entry: &DirEntry) -> bool { entry .file_name() .to_str() .map(|s| s.starts_with(".")) .unwrap_or(false) } fn is_dir(entry: &DirEntry) -> bool { entry.file_type().is_dir() } fn strip_path<'a>(path: &'a Path, to_strip: &Path) -> Result<&'a Path> { path.strip_prefix(to_strip).with_context(|| { format!( "'{}' is not under the specified home path ('{}')!", path.display(), to_strip.display() ) }) } fn path_to_str(path: &Path) -> Result<&str> { path.to_str().with_context(|| { format!( "\ Can't derive a keymapping from path: '{}' \ because it can't be turned to a string ", path.display() ) }) }