137 lines
3.7 KiB
Rust
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);
|
|
}
|
|
}
|