use std::io::{self, Write};
use crate::error::*;
use glob::Pattern;
use serde::{Deserialize, Serialize};
use std::cmp::Ordering;
#[cfg(unix)]
const EXCLUSIONS_PATH: &str = "/etc/klone/exclusions.json";
#[cfg(windows)]
const EXCLUSIONS_PATH: &str = r"C:\ProgramData\klone\exclusions.json";
#[cfg(windows)]
const SYS_EXCLUSIONS: &[&str] = &["*/.git", "*.o", "*.bin", "*.lock"];
#[derive(Serialize, Deserialize, Debug)]
struct Exclusions(Vec<String>);
mod cache;
pub fn is_excluded(pattern: &str) -> Result<bool> {
let binding = cache::get_exclusions()?;
let mut iter = binding.iter().filter(|file| {
Pattern::new(file)
.expect("This should't panic") .matches(pattern)
});
Ok(iter.next().is_some())
}
pub fn add_exclusion() -> Result<()> {
let binding = get_pattern("Add the pattern to exclude: ")?;
let pattern = binding.as_str();
let mut deserialized: Exclusions = serde_json::from_str(
&std::fs::read_to_string(EXCLUSIONS_PATH).map_err(|_| Error {
kind: ErrorKind::FSError,
})?,
)
.map_err(|_| Error {
kind: ErrorKind::JSONParsingError("exclusions".to_string()),
})?;
if Pattern::new(pattern).is_err() {
return Err(Error {
kind: ErrorKind::InvalidPattern(pattern.to_string()),
});
}
if deserialized.0.contains(&pattern.to_string()) {
return Err(Error {
kind: ErrorKind::PatternAlreadyExist,
});
}
deserialized.0.push(pattern.to_string());
std::fs::write(
EXCLUSIONS_PATH,
serde_json::to_string(&deserialized).map_err(|_| Error {
kind: ErrorKind::JSONStringifyingError("exclusions".to_string()),
})?,
)
.map_err(|_| Error {
kind: ErrorKind::FSError,
})?;
Ok(())
}
pub fn list_exclusions() -> Result<()> {
let mut counter = 1;
serde_json::from_str::<Exclusions>(&std::fs::read_to_string(EXCLUSIONS_PATH).map_err(
|_| Error {
kind: ErrorKind::FSError,
},
)?)
.map_err(|_| Error {
kind: ErrorKind::JSONParsingError("exclusions".to_string()),
})?
.0
.iter()
.for_each(|exclusion| {
println!("{} -> {}", counter, exclusion);
counter += 1;
});
Ok(())
}
pub fn remove_exclusion() -> Result<()> {
let binding = get_pattern("Introduce the pattern to remove: ")?;
#[cfg(windows)]
if SYS_EXCLUSIONS.contains(&binding.as_str()) {
println!(
"You can't remove the .git, *.o, *.bin and *.lock exclusion in Windows, because an issue with the permissions. Windows tells they are alwaysblockes"
);
return Ok(());
}
let pattern = binding.as_str();
let binding = serde_json::from_str::<Exclusions>(
&std::fs::read_to_string(EXCLUSIONS_PATH).map_err(|_| Error {
kind: ErrorKind::FSError,
})?,
)
.map_err(|_| Error {
kind: ErrorKind::JSONParsingError("exclusions".to_string()),
})?;
let rest_of_values: Vec<&String> = binding
.0
.iter()
.filter(|stored_pattern| *stored_pattern != pattern)
.collect();
let _ = std::fs::write(
EXCLUSIONS_PATH,
serde_json::to_string(&rest_of_values).map_err(|_| Error {
kind: ErrorKind::JSONStringifyingError("exclsions".to_string()),
})?,
);
match rest_of_values.len().cmp(&binding.0.len()) {
Ordering::Equal => println!("No exclusion was deleted. Make sure you wrote it right"),
Ordering::Less => {
println!("The following exclusion has been removed: {}", pattern);
println!("These are the remaining exclusions:");
list_exclusions()?;
}
Ordering::Greater => println!("WTF!? How did you get here?"),
}
Ok(())
}
fn get_pattern(message: &str) -> Result<String> {
print!("{}", message);
let _ = io::stdout().flush();
let mut input = String::new();
io::stdin().read_line(&mut input).map_err(|_| Error {
kind: ErrorKind::IOError,
})?;
input.pop();
#[cfg(windows)]
input.pop();
Ok(input)
}