Skip to main content

para_audit/
lib.rs

1use colored::Colorize;
2use colored::CustomColor;
3use serde::Deserialize;
4use serde::Deserializer;
5use serde::Serialize;
6use serde::de;
7use std::env;
8use std::fmt;
9use std::fs;
10use std::marker::PhantomData;
11use std::path::{Path, PathBuf};
12pub mod audit;
13pub mod config;
14pub mod launch;
15pub mod layout;
16pub mod search;
17use anyhow::Result;
18use thiserror::Error;
19
20#[derive(Error, Debug)]
21enum ParaError {
22    #[error("{0} environment variable not set")]
23    EnvVariableNotFound(String),
24    #[error("cannot create path for config, set PARA_CONFIG manually")]
25    CannotCreateConfigPath,
26}
27
28fn get_env_path(envvar: &str) -> Result<PathBuf> {
29    Ok(PathBuf::from(&env::var(envvar).map_err(|_| {
30        ParaError::EnvVariableNotFound(envvar.to_string())
31    })?))
32}
33
34pub fn get_home_path() -> Result<PathBuf> {
35    get_env_path("PARA_HOME")
36}
37
38pub fn get_git_path() -> Result<PathBuf> {
39    get_env_path("PARA_GIT")
40}
41
42pub fn get_config_path() -> Result<PathBuf> {
43    if let Ok(path) = get_env_path("PARA_CONFIG") {
44        return Ok(path);
45    }
46    if let Some(mut path) = std::env::home_dir() {
47        path = path.join(".config").join("para-audit");
48        std::fs::create_dir_all(&path)?;
49        path = path.join("config.yaml");
50        return Ok(path);
51    }
52    Err(ParaError::CannotCreateConfigPath)?
53}
54
55pub fn get_root_paths() -> Result<Vec<PathBuf>> {
56    let mut root_paths = vec![];
57    for entry in get_home_path()?
58        .read_dir()
59        .expect("failed to read home dir")
60        .flatten()
61    {
62        let filetype = entry.file_type().expect("failed to extract file type");
63        if filetype.is_dir() {
64            root_paths.push(entry.path());
65        }
66    }
67    Ok(root_paths)
68}
69
70pub fn get_module_paths() -> Result<Vec<PathBuf>> {
71    // Check each top level dir to see if it only contains folders (no files)
72    let root_paths = get_root_paths()?;
73    Ok(root_paths
74        .iter()
75        .flat_map(|root_path| {
76            root_path
77                .read_dir()
78                .expect("failed to read root dirs")
79                .map(|mod_entry| mod_entry.unwrap().path())
80                .collect::<Vec<PathBuf>>()
81        })
82        .filter(|module| module.is_dir())
83        .collect())
84}
85
86pub fn visit_all(path: &PathBuf, cb: &mut dyn FnMut(&PathBuf)) {
87    if path.is_dir() && !path.is_symlink() {
88        for entry in path.read_dir().unwrap() {
89            let entry = entry.unwrap();
90            let path = entry.path();
91            visit_all(&path, cb);
92        }
93    }
94    cb(path);
95}
96
97pub fn eprint_modules(modules: Vec<PathBuf>) {
98    for module in modules {
99        eprintln!("{}", module.display().to_string().italic());
100    }
101}
102
103pub fn print_modules(modules: Vec<PathBuf>, colorised: bool) {
104    for module in modules {
105        if colorised {
106            println!(
107                "{}/{}/{}",
108                module
109                    .parent()
110                    .unwrap()
111                    .parent()
112                    .unwrap()
113                    .display()
114                    .to_string()
115                    .custom_color(CustomColor {
116                        r: 100,
117                        g: 100,
118                        b: 100
119                    }),
120                module
121                    .parent()
122                    .unwrap()
123                    .file_name()
124                    .unwrap()
125                    .to_str()
126                    .unwrap()
127                    .to_string()
128                    .custom_color(CustomColor {
129                        r: 100,
130                        g: 140,
131                        b: 100
132                    }),
133                module
134                    .file_name()
135                    .unwrap()
136                    .to_str()
137                    .unwrap()
138                    .to_string()
139                    .custom_color(CustomColor {
140                        r: 100,
141                        g: 255,
142                        b: 100
143                    }),
144            );
145        } else {
146            println!("{}", module.display());
147        }
148    }
149}
150
151pub fn print_count(item: &str, count: u32) {
152    println!("{:5} {}", count.to_string().yellow(), item.green());
153}
154
155pub fn read_yaml(module: &Path) -> Result<ParaYaml> {
156    // open module/para.yaml file if it exists
157    let f = fs::File::open(module.join("para.yaml"))?;
158    Ok(yaml_serde::from_reader(f)?)
159}
160
161#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
162#[serde(deny_unknown_fields)]
163pub struct ParaYaml {
164    #[serde(default, deserialize_with = "string_or_seq_string")]
165    pub gits: Vec<String>,
166    pub open: Option<Vec<String>>,
167    pub tags: Option<Vec<String>>,
168}
169
170fn string_or_seq_string<'de, D>(deserializer: D) -> Result<Vec<String>, D::Error>
171    where D: Deserializer<'de>
172{
173    struct StringOrVec(PhantomData<Vec<String>>);
174
175    impl<'de> de::Visitor<'de> for StringOrVec {
176        type Value = Vec<String>;
177
178        fn expecting(&self, formatter: &mut fmt::Formatter) -> fmt::Result {
179            formatter.write_str("string or list of strings")
180        }
181
182        fn visit_str<E>(self, value: &str) -> Result<Self::Value, E>
183            where E: de::Error
184        {
185            Ok(vec![value.to_owned()])
186        }
187
188        fn visit_seq<S>(self, visitor: S) -> Result<Self::Value, S::Error>
189            where S: de::SeqAccess<'de>
190        {
191            Deserialize::deserialize(de::value::SeqAccessDeserializer::new(visitor))
192        }
193    }
194
195    deserializer.deserialize_any(StringOrVec(PhantomData))
196}