about summary refs log blame commit diff stats
path: root/pkgs/by-name/lf/lf-make-map/src/mapping/mod.rs
blob: 114fdca0770e73b69cd2e3780c9e833bf53add35 (plain) (tree)
1
2
3
4
5
6
7
8
9


                          

               
                 
                           
                   
                  
 


                                                
 

                                                         

     

                                        

     
             
                                                                                
                                                                 
 





                                                
     
                                                                                

                                                                        
                      





                                                   
           
 

















                                                                                       














                                                                                                    


                                                                       

                                                                          







                                                            




















                                                                                             
                                                                                             
                                            
                                                                                        
                                            
                
                                                                  
          


                            
                                                        

                           




                                                                        
                              
     
use std::{
    fmt::{Display, Write},
    hash::Hash,
};

use log::debug;

pub mod map_tree;

#[derive(Clone, Debug, Eq)]
pub struct MapKey {
    pub key: char,

    resolution: usize,

    /// Part of the path, used to derive the key
    part_path: String,
}

impl Hash for MapKey {
    fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
        self.key.hash(state)
    }
}

impl PartialEq for MapKey {
    fn eq(&self, other: &Self) -> bool {
        self.key == other.key
    }
}

impl MapKey {
    pub fn new_from_part_path(part_path: &str, resolution: usize) -> Vec<Self> {
        let key = Self::part_path_to_key(&part_path, resolution);

        key.chars()
            .map(|ch| Self {
                key: ch,
                resolution,
                part_path: part_path.to_owned(),
            })
            .collect()
    }

    pub fn new_ones_from_path(path: &str, number_of_chars: usize) -> Vec<Self> {
        let key: Vec<MapKey> = path
            .split('/')
            .map(|part| Self::new_from_part_path(part, number_of_chars))
            .flatten()
            .collect();

        debug!(
            "Generated full MapKeys: '{}' -> '{}'",
            path,
            MapKey::display(&key)
        );
        key
    }

    pub fn increment(&self, target_resolution: usize) -> Vec<Self> {
        let new_resolution = target_resolution;

        // debug!("Incrementing: '{}' ('{}')", &self, &self.part_path);

        let added_chars = if new_resolution < self.part_path.len() {
            MapKey::part_path_to_key(&self.part_path, new_resolution)
        } else {
            let mut generated_chars =
                MapKey::part_path_to_key(&self.part_path, self.part_path.len());

            generated_chars.extend(
                (0..(new_resolution - self.part_path.len()))
                    .into_iter()
                    .map(|_| self.part_path.chars().last().expect("This will exists")),
            );

            generated_chars
        };

        let part_path = self.part_path.clone();
        let output: Vec<Self> = added_chars
            .chars()
            .enumerate()
            .map(|(res, ch)| MapKey {
                key: ch,
                resolution: res + 1,
                part_path: part_path.clone(),
            })
            .collect();

        // debug!("Finished increment: '{}' ('{}')", MapKey::display(&output), output[0].part_path);
        output
    }

    pub fn display(values: &[Self]) -> String {
        values.iter().map(|value| value.key.clone()).collect()
    }
    fn part_path_to_key(part: &str, number_of_chars: usize) -> String {
        fn make(pat: char, part: &str, number_of_chars: usize) -> String {
            let mut acc = String::new();

            if !part.split(pat).all(|part| part.len() > 0) {
                panic!(
                    "\
Can't turn this path '{}' to a mapping.
This should not happen, please report the bug!",
                    part
                )
            }

            let mut last_working = None;
            for i in 0..number_of_chars {
                for str in part.split(pat) {
                    if acc.len() != number_of_chars {
                        acc.push(match str.chars().nth(i) {
                            Some(ch) => ch,
                            None => {
                                if let Some(last) = last_working {
                                    str.chars().nth(last).expect("This should always exist")
                                } else {
                                    last_working = Some(i - 1);
                                    str.chars().nth(i - 1).expect("This should always exist")
                                }
                            }
                        })
                    }
                }
            }

            acc
        }

        let value = if part.contains('_') && !part.starts_with('_') && !part.ends_with('_') {
            make('_', part, number_of_chars)
        } else if part.contains('-') && !part.starts_with('-') && !part.ends_with('-') {
            make('-', part, number_of_chars)
        } else {
            part.chars().take(number_of_chars).collect::<String>()
        };

        assert_eq!(
            value.len(),
            number_of_chars,
            "'{}' does not have expected length of: {}",
            value,
            number_of_chars
        );
        value
    }
}

impl Display for MapKey {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        f.write_char(self.key)
    }
}