summaryrefslogblamecommitdiff
path: root/src/pathinfo.rs
blob: be43b6e2fa1c5f38d9aa8bdab8a577cccc25de8f (plain) (tree)























































































































































                                                                                       





























































































                                                                                    
use std::{
    fmt::Display,
    path::PathBuf,
};

use serde::{Deserialize, Serialize};

use crate::{
    backup::BackupId,
    config::Config,
    error::{Error, Result},
};

#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PathInfo {
    pub modified: bool,
    pub is_file: bool,
    rel_location: String,
    location_root: LocationRoot,
    last_modified: BackupId,
    children: Vec<PathInfo>
}

impl PathInfo {
    pub fn from_path(config: &Config, path: &str) -> Result<Self> {
        let locations = Self::parse_location(path, config)?;

        Ok(Self::handle_dir(config, &locations.0, &locations.1)?)
    }

    fn handle_dir(
        config: &Config,
        rel_location: &str,
        location_root: &LocationRoot,
    ) -> Result<Self> {
        println!("Handling {rel_location}");
        let path = Self::get_abs_path(&location_root.to_string(), rel_location);
        Ok(if path.is_dir() {
            let mut modified = false;
            let mut children: Vec<PathInfo> = Vec::new();

            let paths = std::fs::read_dir(path).unwrap();
            for path in paths {
                let pathstr = path.unwrap().path().to_string_lossy().to_string();
                let root = format!("{}/", location_root.to_string());
                let Some(rl) = pathstr.split_once(&root) else {
                    panic!("HUH");
                };
                let handle = Self::handle_dir(config, rl.1, location_root)?;
                if handle.modified {
                    modified = true;
                };
                children.push(handle);
            }
            Self {
                modified,
                is_file: false,
                rel_location: rel_location.to_string(),
                location_root: location_root.clone(),
                last_modified: "".to_string(),
                children
            }
        } else {
            Self::from_file(rel_location, location_root.clone())?
        })
    }

    fn from_file(rel_location: &str, location_root: LocationRoot) -> Result<Self> {
        println!("From file {rel_location}");

        let modified = false;

        Ok(Self {
            rel_location: rel_location.to_string(),
            location_root,
            modified,
            last_modified: "".to_string(),
            is_file: true,
            children: Vec::new()
        })
    }

    pub fn get_absolute_path(&self) -> PathBuf {
        Self::get_abs_path(&self.location_root.to_string(), &self.rel_location)
    }

    fn get_abs_path(location_root: &str, rel_location: &str) -> PathBuf {
        let path = format!("{}/{}", location_root, rel_location);
        PathBuf::from(path)
    }

    fn parse_location(value: &str, config: &Config) -> Result<(String, LocationRoot)> {
        let Some(split) = value.split_once('/') else {
            return Err(Error::InvalidDirectory(value.to_string()));
        };
        if split.0.starts_with('~') {
            if config.user.len() != 1 {
                return Err(Error::MultiUser);
            }
            return Ok((
                split.1.to_string(),
                LocationRoot::User(config.user[0].clone()),
            ));
        };
        Ok((
            split.1.to_string(),
            LocationRoot::from_op_str(split.0, config)?,
        ))
    }
}

#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub enum LocationRoot {
    User(String),
    Custom(String),
    SystemSettings,
    Root,
}

impl Display for LocationRoot {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        match self {
            LocationRoot::User(user) => write!(f, "/home/{user}"),
            LocationRoot::Custom(loc) => write!(f, "{loc}"),
            LocationRoot::SystemSettings => write!(f, "/etc"),
            LocationRoot::Root => write!(f, "/"),
        }
    }
}

impl LocationRoot {
    fn from_op_str(value: &str, config: &Config) -> Result<Self> {
        let split_str = value.split_once(':');
        let Some(split_op) = split_str else {
            return Err(Error::NoIndex);
        };
        match split_op.0 {
            "u" => Ok(Self::User(split_op.1.to_string())),
            "s" => Ok(Self::SystemSettings),
            "r" => Ok(Self::Root),
            "c" => Ok(Self::Custom(
                config
                    .custom_directories
                    .get(split_op.1)
                    .ok_or_else(|| Error::CustomDirectory(split_op.1.to_string()))?
                    .to_string(),
            )),
            _ => Err(Error::InvalidIndex(split_op.0.to_string())),
        }
    }
}

// #[cfg(test)]
// mod tests {
//     use crate::{
//         config::Config,
//         error::{Error, Result},
//         pathinfo::PathInfo,
//     };
// 
//     use super::LocationRoot;
// 
//     #[test]
//     fn from_op_str() -> Result<()> {
//         let mut config = Config::default();
//         config
//             .custom_directories
//             .insert("test".to_string(), "/usr/local/test".to_string());
// 
//         let mut values: Vec<(&str, Result<LocationRoot>)> = Vec::new();
//         values.push(("u:test", Ok(LocationRoot::User("test".to_string()))));
//         values.push(("s:", Ok(LocationRoot::SystemSettings)));
//         values.push(("r:", Ok(LocationRoot::Root)));
//         values.push((
//             "c:test",
//             Ok(LocationRoot::Custom("/usr/local/test".to_string())),
//         ));
//         values.push(("c:rest", Err(Error::CustomDirectory("rest".to_string()))));
//         values.push(("t:test/", Err(Error::InvalidIndex("t".to_string()))));
//         values.push((
//             "test:test/usr",
//             Err(Error::InvalidIndex("test".to_string())),
//         ));
//         values.push(("/usr/local/test", Err(Error::NoIndex)));
//         values.push(("c/usr/local/test", Err(Error::NoIndex)));
// 
//         for value in values {
//             print!("Testing {value:?}");
//             assert_eq!(LocationRoot::from_op_str(value.0, &config), value.1);
//             println!("\rTesting {value:?} ✓");
//         }
// 
//         Ok(())
//     }
// 
//     #[test]
//     fn parse_location() -> Result<()> {
//         let mut config = Config::default();
//         config.user.push("test".to_string());
//         config
//             .custom_directories
//             .insert("test".to_string(), "/usr/local/test".to_string());
// 
//         let mut values: Vec<(&str, Result<(String, LocationRoot)>)> = Vec::new();
//         values.push((
//             "~/.config/nvim",
//             Ok((
//                 ".config/nvim".to_string(),
//                 LocationRoot::User("test".to_string()),
//             )),
//         ));
//         values.push((
//             "u:test/.config/nvim",
//             Ok((
//                 ".config/nvim".to_string(),
//                 LocationRoot::User("test".to_string()),
//             )),
//         ));
//         values.push((
//             "r:/.config/nvim",
//             Ok((".config/nvim".to_string(), LocationRoot::Root)),
//         ));
//         values.push((
//             "r:/.config/nvim",
//             Ok((".config/nvim".to_string(), LocationRoot::Root)),
//         ));
//         values.push((
//             "s:/.config/nvim",
//             Ok((".config/nvim".to_string(), LocationRoot::SystemSettings)),
//         ));
//         values.push((
//             "c:test/.config/nvim",
//             Ok((
//                 ".config/nvim".to_string(),
//                 LocationRoot::Custom("/usr/local/test".to_string()),
//             )),
//         ));
// 
//         for value in values {
//             print!("Testing {value:?}");
//             assert_eq!(PathInfo::parse_location(&value.0, &config), value.1);
//             println!("\rTesting {value:?} ✓");
//         }
//         Ok(())
//     }
// }