From 47e3b82d0f8c9c4abcaf8588764fa934446dbdc8 Mon Sep 17 00:00:00 2001 From: Benedikt Peetz Date: Sun, 5 May 2024 14:02:09 +0200 Subject: feat(pkgs/lf-make-map): Change the key to custom type and add visuals --- sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs | 16 +- .../pkgs/lf-make-map/src/generator/mod.rs.old | 165 --------------------- .../pkgs/lf-make-map/src/mapping/map_tree.rs | 107 ++++++++++--- sys/nixpkgs/pkgs/lf-make-map/src/mapping/mod.rs | 145 ++++++++++-------- 4 files changed, 181 insertions(+), 252 deletions(-) delete mode 100644 sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs.old (limited to 'sys') diff --git a/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs b/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs index 48df027f..c03b33dc 100644 --- a/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs +++ b/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs @@ -1,10 +1,10 @@ use std::path::PathBuf; use anyhow::{Context, Result}; -use log::{debug, info, trace}; +use log::trace; use walkdir::{DirEntry, WalkDir}; -use crate::mapping::{map_tree::MappingTree, Mapping}; +use crate::mapping::{map_tree::MappingTree, MapKey, Mapping}; pub struct MappingsGenerator { mappings: MappingTree, @@ -35,9 +35,15 @@ impl MappingsGenerator { trace!("Processed '{}'..", directory.path().display()); let mapping = Mapping::new(&home_path, directory.path().to_path_buf()); - mappings - .insert(&mapping.key.clone(), mapping) - .context("Failed to insert a key")?; + let mapping_key = mapping.key.clone(); + mappings.insert(&mapping_key, mapping).with_context(|| { + format!( + "Failed to insert '{}' for path: '{}'\nMapTree is now: \n{}", + MapKey::display(&mapping_key), + directory.path().display(), + mappings, + ) + })?; } } todo!() diff --git a/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs.old b/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs.old deleted file mode 100644 index 406b1996..00000000 --- a/sys/nixpkgs/pkgs/lf-make-map/src/generator/mod.rs.old +++ /dev/null @@ -1,165 +0,0 @@ -use std::{ - collections::HashMap, - path::{Path, PathBuf}, -}; - -use anyhow::{bail, Context, Result}; -use futures::{Stream, StreamExt, TryStreamExt}; -use log::info; -use tokio::{ - fs::{self, DirEntry}, - io, - sync::mpsc::{self, Receiver, Sender}, - task::JoinHandle, -}; -use tokio_stream::wrappers::{ReadDirStream, ReceiverStream}; - -pub struct MappingGenerator { - current_mappings: HashMap, - paths_to_process: Vec, -} - -pub struct MappingGeneratorHelper { - tx: Sender<(PathBuf, oneshotSender)>, - handle: JoinHandle<()>, - done: Vec, -} - -impl MappingGeneratorHelper { - pub fn new() -> Self { - let (rx, tx) = mpsc::channel(100); - - let handle = tokio::spawn(async move { - while let Some(dir) = rx.recv().await { - info!("processing '{}'..", dir.display()); - get_dir(dir); - } - }); - - Self { tx, handle } - } - - pub async fn process(&self, dir: PathBuf) -> Result<()> { - let (tx, rx) = - self.tx.send(dir).await?; - Ok(()) - } -} - -impl MappingGenerator { - pub async fn new(directories_to_scan: Vec, depth: usize) -> Result { - let cleaned_directories: Vec = directories_to_scan - .into_iter() - .map(check_dir) - .collect::>()?; - - let helper = MappingGeneratorHelper::new(); - - cleaned_directories - .into_iter() - .for_each(|dir| helper.process(dir)); - - info!( - "Will process:\n {}", - all_directories - .iter() - .map(|pat| pat.display().to_string()) - .collect::>() - .join("\n ") - ); - Ok(Self { - current_mappings: HashMap::new(), - paths_to_process: all_directories, - }) - } -} - -fn check_dir(dir: PathBuf) -> Result { - match dir.metadata() { - Ok(_) => Ok(dir), - Err(e) => bail!( - "'{}' is not a valid path; Error was: '{}'", - dir.display(), - e - ), - } -} - -pub async fn get_dir(dir: PathBuf, current_depth: usize, max_depth: usize) -> Result> { - let (tx, rx) = mpsc::channel(100); - - let handle = tokio::spawn(async move { get_dir_recursive(dir, current_depth, max_depth, tx) }); - - let out = ReceiverStream::new(rx).collect::>().await; - handle.await?; - Ok(out) -} - -async fn get_dir_recursive( - dir: PathBuf, - current_depth: usize, - max_depth: usize, - tx: Sender, -) -> Result<()> { - if dir.is_dir() && current_depth != max_depth { - tx.send(dir).await?; - - match fs::read_dir(&dir).await { - Ok(directories) => { - let mut handles: Vec>> = vec![]; - while let Some(entry) = directories - .next_entry() - .await - .with_context(|| format!("Failed to read directory: '{}'", dir.display()))? - { - let tx_new = tx.clone(); - handles.push(tokio::spawn(async move { - get_dir_recursive(entry.path(), current_depth + 1, max_depth, tx_new) - .await - .with_context(|| { - format!("Failed to get child directories to '{}'", dir.display()) - })?; - - Ok(()) - })); - } - - let out: Vec<_> = tokio_stream::iter(handles) - .then(|handle| async move { handle.await }) - .collect() - .await; - - // I have no idea what happened here to the type system - for i in out { - i?? - } - - Ok(()) - } - - Err(e) => { - bail!( - "Unable to read directory {}, skipping; error: {}", - dir.display(), - e - ); - } - } - } else { - return Ok(()); - } -} - -#[cfg(test)] -mod test { - use std::path::PathBuf; - - use super::get_dir; - - #[test] - fn test_get_dir() { - let dirs = get_dir(PathBuf::from("~/repos")); - let expected_dirs = vec![PathBuf::from("~/repos/rust")]; - assert_eq!(dirs, expected_dirs); - } -} diff --git a/sys/nixpkgs/pkgs/lf-make-map/src/mapping/map_tree.rs b/sys/nixpkgs/pkgs/lf-make-map/src/mapping/map_tree.rs index 44165ed1..90296044 100644 --- a/sys/nixpkgs/pkgs/lf-make-map/src/mapping/map_tree.rs +++ b/sys/nixpkgs/pkgs/lf-make-map/src/mapping/map_tree.rs @@ -1,6 +1,6 @@ -use std::collections::HashMap; +use std::{collections::HashMap, fmt::Display, path::PathBuf}; -use super::{error, Mapping}; +use super::{error, MapKey, Mapping}; /// A prefix tree pub struct MappingTree { @@ -9,33 +9,33 @@ pub struct MappingTree { #[derive(Clone)] pub struct Node { - children: HashMap, + children: HashMap, value: Option, /// The key needed to get to this node - location: String, + location: Vec, } impl MappingTree { pub fn new() -> Self { Self { - root: Node::new(String::new(), None), + root: Node::new(vec![], None), } } /// Returns the node at the key, otherwise None - pub fn get(&self, key: &str) -> Option<&Node> { + pub fn get(&self, key: &[MapKey]) -> Option<&Node> { let mut current_node = &self.root; - for ch in key.chars() { + for ch in key.iter() { current_node = current_node.children.get(&ch)? } Some(current_node) } /// Returns the node at the key, otherwise None. The node can be changed - pub fn get_mut(&mut self, key: &str) -> Option<&mut Node> { + pub fn get_mut(&mut self, key: &[MapKey]) -> Option<&mut Node> { let mut current_node = &mut self.root; - for ch in key.chars() { + for ch in key.iter() { current_node = current_node.children.get_mut(&ch)? } @@ -43,9 +43,9 @@ impl MappingTree { } /// Returns the node at the key, otherwise the last node that matched. - pub fn try_get(&self, key: &str) -> &Node { + pub fn try_get(&self, key: &[MapKey]) -> &Node { let mut current_node = &self.root; - for ch in key.chars() { + for ch in key.iter() { if let Some(node) = current_node.children.get(&ch) { current_node = node; } else { @@ -56,20 +56,22 @@ impl MappingTree { current_node } - pub fn insert(&mut self, key: &str, mapping: Mapping) -> Result<(), error::Error> { + pub fn insert(&mut self, key: &[MapKey], mapping: Mapping) -> Result<(), error::Error> { let node = self.try_get(key).clone(); - if node.location.as_str() != key { - let needed_nodes_key = key.trim_start_matches(node.location.as_str()); - let needed_nodes_length = needed_nodes_key.chars().count(); + if node.location.as_slice() != key { + let needed_nodes_key = key + .strip_prefix(node.location.as_slice()) + .expect("The node's location is a prefix"); + let needed_nodes_length = needed_nodes_key.iter().count(); let mut current_node = self .get_mut(&node.location) .expect("This should always exists"); let mut current_location = node.location.clone(); - let mut counter = 0; + let mut counter = 1; - for ch in needed_nodes_key.chars() { - current_location.push(ch); + for ch in needed_nodes_key.iter() { + current_location.push(ch.to_owned()); let next_node = if counter == needed_nodes_length { Node::new(current_location.clone(), Some(mapping.clone())) @@ -77,7 +79,7 @@ impl MappingTree { Node::new(current_location.clone(), None) }; - current_node.children.insert(ch, next_node); + current_node.children.insert(ch.to_owned(), next_node); current_node = current_node .children .get_mut(&ch) @@ -85,7 +87,7 @@ impl MappingTree { counter += 1; } } else { - return Err(error::Error::NodeExits(key.to_owned())); + return Err(error::Error::NodeExits(MapKey::display(key))); } Ok(()) @@ -93,7 +95,7 @@ impl MappingTree { } impl Node { - pub fn new(location: String, mapping: Option) -> Self { + pub fn new(location: Vec, mapping: Option) -> Self { Self { children: HashMap::new(), location, @@ -101,3 +103,66 @@ impl Node { } } } + +impl Display for MappingTree { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + fn write_node( + node: &Node, + indention: String, + has_parent: bool, + is_last: bool, + f: &mut std::fmt::Formatter<'_>, + ) -> std::fmt::Result { + let bullet = if has_parent { + if is_last { + String::from("└── ") + } else { + String::from("├── ") + } + } else { + String::new() + }; + + let node_value = if let Some(value) = &node.value { + value.to_string() + } else { + "[No Value]".to_owned() + }; + + let new_idention = if !has_parent { + String::new() + } else if is_last { + indention.replace('│', " ") + " " + } else { + indention.clone() + "│ " + }; + + write!( + f, + "{}{}\x1b[1;33m{}\x1b[0m: {}\n", + indention, + bullet, + MapKey::display(&node.location), + node_value, + )?; + + let value_length = node.children.len(); + let mut counter = 1; + + for child in node.children.values() { + if counter == value_length { + write_node(child, new_idention.clone(), true, true, f)?; + } else { + write_node(child, new_idention.clone(), true, false, f)?; + }; + counter += 1; + } + + Ok(()) + } + + write_node(&self.root, String::new(), false, false, f)?; + + Ok(()) + } +} diff --git a/sys/nixpkgs/pkgs/lf-make-map/src/mapping/mod.rs b/sys/nixpkgs/pkgs/lf-make-map/src/mapping/mod.rs index 7de1ca5d..d05d3417 100644 --- a/sys/nixpkgs/pkgs/lf-make-map/src/mapping/mod.rs +++ b/sys/nixpkgs/pkgs/lf-make-map/src/mapping/mod.rs @@ -1,4 +1,7 @@ -use std::path::{Path, PathBuf}; +use std::{ + fmt::Display, + path::{Path, PathBuf}, +}; use log::debug; @@ -11,15 +14,42 @@ pub struct Mapping { pub keys: usize, - pub key: String, + pub key: Vec, } + +#[derive(Clone, Debug, PartialEq, PartialOrd, Ord, Hash, Eq)] +pub struct MapKey { + value: String, +} + +impl MapKey { + pub fn new(value: String) -> Self { + Self { value } + } + pub fn display(values: &[Self]) -> String { + values.iter().map(|value| value.value.clone()).collect() + } +} + +impl Display for MapKey { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + f.write_str(&self.value) + } +} + +impl Display for Mapping { + fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { + write!(f, "{}", self.raw_path.display()) + } +} + impl Mapping { pub fn new(home_path: &Path, initial_path: PathBuf) -> Mapping { let raw_path = initial_path .strip_prefix(home_path) .expect("Must always be under the `home_path`"); - let key = Self::path_to_key(raw_path.to_str().expect("Should be a valid &str")); + let key = Self::path_to_keys(raw_path.to_str().expect("Should be a valid &str")); Self { raw_path: raw_path.to_owned(), @@ -28,71 +58,64 @@ impl Mapping { } } - fn path_to_key(path: &str) -> String { - let key: String = path - .split('/') - .map(|part| part.chars().nth(0).expect("Must have a first element")) - .collect(); - debug!("'{}' -> '{}'", path, key); + fn path_to_keys(path: &str) -> Vec { + let key: Vec = path.split('/').map(Self::part_path_to_key).collect(); + debug!("Will insert: '{}' -> '{}'", path, MapKey::display(&key)); key } -} -pub fn gen_hot_key(path: &Path, base_path: &Path, amount_of_chars: usize) -> String { - let path_filename_as_str = path - .file_name() - .expect("All paths here should have a file name") - .to_str() - .expect("The OSstr should be convertible"); - - let mut return_val = String::from("g"); - if path != base_path { - return_val.push( - base_path - .file_name() - .expect("All paths here should have a file name") - .to_str() - .expect("The OSstr should be convertible") - .chars() - .nth(0) - .expect("All names should have a first char"), - ); - } - if path_filename_as_str.contains("_") { - path_filename_as_str.split("_").for_each(|a| { - return_val.push( - a.chars() - .nth(0) - .expect("All names should have a first char"), - ) - }); - } else { - if path == base_path { - return_val.push( - path_filename_as_str - .chars() - .nth(0) - .expect("All names should have a first char"), - ); + fn part_path_to_key(part: &str) -> MapKey { + let value = if part.contains('_') { + part.split('_').filter_map(|ch| ch.chars().nth(0)).collect() + } else if part.contains('-') { + part.split('-').filter_map(|ch| ch.chars().nth(0)).collect() } else { - for a in 0..amount_of_chars { - return_val.push(if let Some(b) = path_filename_as_str.chars().nth(a) { - b - } else { - path_filename_as_str - .chars() - .nth(0) - .expect("All names should have a first char") - }); - } - } - } - if path == base_path { - return_val.push('.'); + part.chars() + .nth(0) + .expect("Must have a first element") + .to_string() + }; + MapKey::new(value) } - return_val } +// pub fn gen_hot_key(path: &Path, base_path: &Path, amount_of_chars: usize) -> String { +// let mut return_val = String::from("g"); +// if path_filename_as_str.contains("_") { +// path_filename_as_str.split("_").for_each(|a| { +// return_val.push( +// a.chars() +// .nth(0) +// .expect("All names should have a first char"), +// ) +// }); +// } else { +// if path == base_path { +// return_val.push( +// path_filename_as_str +// .chars() +// .nth(0) +// .expect("All names should have a first char"), +// ); +// } else { +// for a in 0..amount_of_chars { +// return_val.push(if let Some(b) = path_filename_as_str.chars().nth(a) { +// b +// } else { +// path_filename_as_str +// .chars() +// .nth(0) +// .expect("All names should have a first char") +// }); +// } +// } +// } +// if path == base_path { +// return_val.push('.'); +// } +// return_val +// } + #[cfg(test)] mod tests { use super::*; -- cgit 1.4.1