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 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 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}