Make cache configurable (#6)

This commit is contained in:
Charlie Marsh 2022-08-13 11:18:20 -04:00 committed by GitHub
parent 0a034ddb57
commit 35d1d24399
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 101 additions and 49 deletions

View File

@ -4,13 +4,15 @@ use std::time::{Duration, Instant};
use ::rust_python_linter::fs::collect_python_files; use ::rust_python_linter::fs::collect_python_files;
use ::rust_python_linter::linter::check_path; use ::rust_python_linter::linter::check_path;
use ::rust_python_linter::logging::set_up_logging;
use ::rust_python_linter::message::Message;
use ::rust_python_linter::timestamped_println;
use anyhow::Result; use anyhow::Result;
use clap::{Parser, ValueHint}; use clap::{Parser, ValueHint};
use colored::Colorize; use colored::Colorize;
use log::{debug, error}; use log::{debug, error};
use notify::{watcher, RecursiveMode, Watcher}; use notify::{watcher, RecursiveMode, Watcher};
use rayon::prelude::*; use rayon::prelude::*;
use rust_python_linter::message::Message;
use walkdir::DirEntry; use walkdir::DirEntry;
#[derive(Debug, Parser)] #[derive(Debug, Parser)]
@ -23,44 +25,11 @@ struct Cli {
verbose: bool, verbose: bool,
#[clap(short, long, action)] #[clap(short, long, action)]
watch: bool, watch: bool,
#[clap(short, long, action)]
no_cache: bool,
} }
#[macro_export] fn run_once(files: &[PathBuf], cache: bool) -> Result<Vec<Message>> {
macro_rules! timestamped_println {
($($arg:tt)*) => {
println!(
"[{}] {}",
chrono::Local::now()
.format("%H:%M:%S %p")
.to_string()
.dimmed(),
format_args!($($arg)*)
)
}
}
fn set_up_logging(verbose: bool) -> Result<()> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(if verbose {
log::LevelFilter::Debug
} else {
log::LevelFilter::Info
})
.chain(std::io::stdout())
.apply()
.map_err(|e| e.into())
}
fn run_once(files: &[PathBuf]) -> Result<Vec<Message>> {
// Collect all the files to check. // Collect all the files to check.
let start = Instant::now(); let start = Instant::now();
let files: Vec<DirEntry> = files.iter().flat_map(collect_python_files).collect(); let files: Vec<DirEntry> = files.iter().flat_map(collect_python_files).collect();
@ -71,7 +40,7 @@ fn run_once(files: &[PathBuf]) -> Result<Vec<Message>> {
let messages: Vec<Message> = files let messages: Vec<Message> = files
.par_iter() .par_iter()
.map(|entry| { .map(|entry| {
check_path(entry.path()).unwrap_or_else(|e| { check_path(entry.path(), &cache.into()).unwrap_or_else(|e| {
error!("Failed to check {}: {e:?}", entry.path().to_string_lossy()); error!("Failed to check {}: {e:?}", entry.path().to_string_lossy());
vec![] vec![]
}) })
@ -123,7 +92,7 @@ fn main() -> Result<()> {
clearscreen::clear()?; clearscreen::clear()?;
timestamped_println!("Starting linter in watch mode...\n"); timestamped_println!("Starting linter in watch mode...\n");
let messages = run_once(&cli.files)?; let messages = run_once(&cli.files, !cli.no_cache)?;
report_continuously(&messages)?; report_continuously(&messages)?;
// Configure the file watcher. // Configure the file watcher.
@ -140,14 +109,14 @@ fn main() -> Result<()> {
clearscreen::clear()?; clearscreen::clear()?;
timestamped_println!("File change detected...\n"); timestamped_println!("File change detected...\n");
let messages = run_once(&cli.files)?; let messages = run_once(&cli.files, !cli.no_cache)?;
report_continuously(&messages)?; report_continuously(&messages)?;
} }
Err(e) => return Err(e.into()), Err(e) => return Err(e.into()),
} }
} }
} else { } else {
let messages = run_once(&cli.files)?; let messages = run_once(&cli.files, !cli.no_cache)?;
report_once(&messages)?; report_once(&messages)?;
} }

View File

@ -26,6 +26,42 @@ struct CheckResult {
messages: Vec<Message>, messages: Vec<Message>,
} }
pub enum Mode {
ReadWrite,
ReadOnly,
WriteOnly,
None,
}
impl Mode {
fn allow_read(&self) -> bool {
match self {
Mode::ReadWrite => true,
Mode::ReadOnly => true,
Mode::WriteOnly => false,
Mode::None => false,
}
}
fn allow_write(&self) -> bool {
match self {
Mode::ReadWrite => true,
Mode::ReadOnly => false,
Mode::WriteOnly => true,
Mode::None => false,
}
}
}
impl From<bool> for Mode {
fn from(value: bool) -> Self {
match value {
true => Mode::ReadWrite,
false => Mode::None,
}
}
}
fn cache_dir() -> &'static str { fn cache_dir() -> &'static str {
"./.cache" "./.cache"
} }
@ -34,7 +70,11 @@ fn cache_key(path: &Path) -> Cow<str> {
path.to_string_lossy() path.to_string_lossy()
} }
pub fn get(path: &Path) -> Option<Vec<Message>> { pub fn get(path: &Path, mode: &Mode) -> Option<Vec<Message>> {
if !mode.allow_read() {
return None;
};
match cacache::read_sync(cache_dir(), cache_key(path)) { match cacache::read_sync(cache_dir(), cache_key(path)) {
Ok(encoded) => match path.metadata() { Ok(encoded) => match path.metadata() {
Ok(m) => match bincode::deserialize::<CheckResult>(&encoded[..]) { Ok(m) => match bincode::deserialize::<CheckResult>(&encoded[..]) {
@ -53,7 +93,11 @@ pub fn get(path: &Path) -> Option<Vec<Message>> {
None None
} }
pub fn set(path: &Path, messages: &[Message]) { pub fn set(path: &Path, messages: &[Message], mode: &Mode) {
if !mode.allow_write() {
return;
};
if let Ok(metadata) = path.metadata() { if let Ok(metadata) = path.metadata() {
let check_result = CheckResultRef { let check_result = CheckResultRef {
metadata: &CacheMetadata { metadata: &CacheMetadata {

View File

@ -1,7 +1,8 @@
mod cache; mod cache;
mod checker; pub mod checker;
pub mod fs; pub mod fs;
pub mod linter; pub mod linter;
pub mod logging;
pub mod message; pub mod message;
mod parser; pub mod parser;
mod visitor; pub mod visitor;

View File

@ -4,6 +4,7 @@ use anyhow::Result;
use log::debug; use log::debug;
use serde::{Deserialize, Serialize}; use serde::{Deserialize, Serialize};
use crate::cache::Mode;
use crate::checker::check_ast; use crate::checker::check_ast;
use crate::message::Message; use crate::message::Message;
use crate::{cache, parser}; use crate::{cache, parser};
@ -20,9 +21,9 @@ struct CheckResult {
messages: Vec<Message>, messages: Vec<Message>,
} }
pub fn check_path(path: &Path) -> Result<Vec<Message>> { pub fn check_path(path: &Path, mode: &Mode) -> Result<Vec<Message>> {
// Check the cache. // Check the cache.
if let Some(messages) = cache::get(path) { if let Some(messages) = cache::get(path, mode) {
debug!("Cache hit for: {}", path.to_string_lossy()); debug!("Cache hit for: {}", path.to_string_lossy());
return Ok(messages); return Ok(messages);
} }
@ -37,7 +38,7 @@ pub fn check_path(path: &Path) -> Result<Vec<Message>> {
filename: path.to_string_lossy().to_string(), filename: path.to_string_lossy().to_string(),
}) })
.collect(); .collect();
cache::set(path, &messages); cache::set(path, &messages, mode);
Ok(messages) Ok(messages)
} }

37
src/logging.rs Normal file
View File

@ -0,0 +1,37 @@
use anyhow::Result;
use fern;
#[macro_export]
macro_rules! timestamped_println {
($($arg:tt)*) => {
println!(
"[{}] {}",
chrono::Local::now()
.format("%H:%M:%S %p")
.to_string()
.dimmed(),
format_args!($($arg)*)
)
}
}
pub fn set_up_logging(verbose: bool) -> Result<()> {
fern::Dispatch::new()
.format(|out, message, record| {
out.finish(format_args!(
"{}[{}][{}] {}",
chrono::Local::now().format("[%Y-%m-%d][%H:%M:%S]"),
record.target(),
record.level(),
message
))
})
.level(if verbose {
log::LevelFilter::Debug
} else {
log::LevelFilter::Info
})
.chain(std::io::stdout())
.apply()
.map_err(|e| e.into())
}