gclone/src/cache.rs

137 lines
3.7 KiB
Rust

//! This module contains the cache manager.
use std::fs::File;
use std::io::{stdin, stdout, BufRead, BufReader, Write};
use std::num::Wrapping;
use std::path::PathBuf;
use colored::*;
use walkdir::WalkDir;
use crate::git::GCLONE_PATH;
use crate::{Error, Result};
/// Flushes the stdout.
fn flush_stdout() {
stdout().flush().ok();
}
/// The cache of gclone.
///
/// When running the command `cdg`, if the computer just booted, finding all the directories can be
/// quite slow. The cache contains the list of entries in the git directory to avoid re-finding
/// them every time.
pub struct Cache(Vec<String>);
impl Cache {
/// Generates the cache by traversing the files in the git directory.
pub fn generate() -> Cache {
Cache(
WalkDir::new(&*GCLONE_PATH)
.max_depth(3)
.into_iter()
.filter_map(|x| x.ok())
.map(|x| x.path().display().to_string())
.collect(),
)
}
/// Reads the cache file.
pub fn read() -> Result<Cache> {
let mut path = PathBuf::from(&*GCLONE_PATH);
path.push(".cdgcache");
let file = File::open(&path)?;
let file = BufReader::new(file);
let mut values = vec![];
for line in file.lines() {
if let Ok(l) = line {
values.push(l);
}
}
Ok(Cache(values))
}
/// Reads the cache file, and if it failed, generate a new cache.
pub fn read_or_generate() -> Cache {
match Cache::read() {
Ok(c) => c,
Err(_) => Cache::generate(),
}
}
/// Writes the current content of the cache to the cache file.
pub fn write(&self) -> Result<()> {
let mut path = PathBuf::from(&*GCLONE_PATH);
path.push(".cdgcache");
let mut file = File::create(path)?;
for line in &self.0 {
writeln!(file, "{}", line)?;
}
Ok(())
}
/// Search a directory in the cache, and returns all matching directories.
pub fn find(&self, dirname: &str) -> Vec<String> {
let dirname = &format!("/{}", dirname);
let mut matches = vec![];
for line in &self.0 {
if line.ends_with(dirname) {
matches.push(line.clone());
}
}
matches
}
/// Searches a directory, if multiple where found, prompt user for answer.
pub fn find_interactive(&self, dirname: &str) -> Result<String> {
let matches = self.find(dirname);
match matches.len() {
0 => Err(Error::NoSuchDirectory),
1 => Ok(matches[0].clone()),
len => {
info!("{}", "multiple entries found, please select one");
for (i, j) in matches.iter().enumerate() {
eprintln!(
"{} {}",
&format!("[{}]", i + 1).bold().blue(),
&format!("{}", j).yellow()
);
}
eprint!("{}", "Enter your choice: ".bold());
flush_stdout();
let mut string = String::new();
stdin().read_line(&mut string)?;
// Pop the trailing \n of the string
string.pop();
// Use wrapping to move 0 to std::usize::MAX which is ok.
let value = (Wrapping(string.parse::<usize>()?) - Wrapping(1)).0;
if value >= len {
return Err(Error::OutsideRange);
}
Ok(matches[value].clone())
}
}
}
/// Appends a value to the cache.
pub fn append(&mut self, value: String) {
self.0.push(value);
}
}