#[cfg(unix)]
#[global_allocator]
static GLOBAL: tikv_jemallocator::Jemalloc = tikv_jemallocator::Jemalloc;
#[cfg(windows)]
#[global_allocator]
static GLOBAL_WIN: mimalloc::MiMalloc = mimalloc::MiMalloc;
use std::path::Path;
use std::process::ExitCode;
use zccache::core::NormalizedPath;
use clap::{Parser, Subcommand, ValueEnum};
use zccache::fingerprint::{
walk_files, walk_files_glob, FingerprintError, HashCache, TwoLayerCache,
};
#[derive(Debug, Parser)]
#[command(name = "zccache-fp", version, about)]
struct Cli {
#[arg(long)]
cache_file: NormalizedPath,
#[arg(long, value_enum, default_value_t = CacheType::TwoLayer)]
cache_type: CacheType,
#[command(subcommand)]
command: Commands,
}
#[derive(Debug, Clone, ValueEnum)]
enum CacheType {
Hash,
TwoLayer,
}
#[derive(Debug, Subcommand)]
enum Commands {
Check {
#[arg(long, default_value = ".")]
root: NormalizedPath,
#[arg(long, conflicts_with = "include")]
ext: Vec<String>,
#[arg(long, conflicts_with = "ext")]
include: Vec<String>,
#[arg(long)]
exclude: Vec<String>,
},
#[command(name = "mark-success")]
MarkSuccess,
#[command(name = "mark-failure")]
MarkFailure,
Invalidate,
}
fn main() -> ExitCode {
let cli = Cli::parse();
match run(cli) {
Ok(code) => code,
Err(e) => {
eprintln!("error: {e}");
ExitCode::from(2)
}
}
}
fn run(cli: Cli) -> Result<ExitCode, FingerprintError> {
match cli.command {
Commands::Check {
root,
ext,
include,
exclude,
} => run_check(
&cli.cache_file,
&cli.cache_type,
&root,
&ext,
&include,
&exclude,
),
Commands::MarkSuccess => run_mark(&cli.cache_file, &cli.cache_type, true),
Commands::MarkFailure => run_mark(&cli.cache_file, &cli.cache_type, false),
Commands::Invalidate => run_invalidate(&cli.cache_file, &cli.cache_type),
}
}
fn run_check(
cache_file: &Path,
cache_type: &CacheType,
root: &Path,
ext: &[String],
include: &[String],
exclude: &[String],
) -> Result<ExitCode, FingerprintError> {
let fast = match cache_type {
CacheType::Hash => HashCache::new(cache_file.to_path_buf()).try_skip_fast(root)?,
CacheType::TwoLayer => TwoLayerCache::new(cache_file.to_path_buf()).try_skip_fast(root)?,
};
if let Some(decision) = fast {
eprintln!("{decision}");
return Ok(if decision.should_skip() {
ExitCode::from(1)
} else {
ExitCode::SUCCESS
});
}
let files = if !include.is_empty() {
let inc: Vec<&str> = include.iter().map(String::as_str).collect();
let exc: Vec<&str> = exclude.iter().map(String::as_str).collect();
walk_files_glob(root, &inc, &exc)?
} else {
let exts: Vec<&str> = ext.iter().map(String::as_str).collect();
let dirs: Vec<&str> = exclude.iter().map(String::as_str).collect();
walk_files(root, &exts, &dirs)?
};
eprintln!("scanned {} files from {}", files.len(), root.display());
let decision = match cache_type {
CacheType::Hash => HashCache::new(cache_file.to_path_buf()).check(&files)?,
CacheType::TwoLayer => TwoLayerCache::new(cache_file.to_path_buf()).check(&files)?,
};
eprintln!("{decision}");
Ok(if decision.should_skip() {
ExitCode::from(1)
} else {
ExitCode::SUCCESS
})
}
fn run_mark(
cache_file: &Path,
cache_type: &CacheType,
success: bool,
) -> Result<ExitCode, FingerprintError> {
let cache_type = match zccache::fingerprint::detect_pending_type(cache_file) {
Some("hash") => CacheType::Hash,
Some("two-layer") => CacheType::TwoLayer,
_ => cache_type.clone(),
};
match (&cache_type, success) {
(CacheType::Hash, true) => HashCache::new(cache_file.to_path_buf()).mark_success()?,
(CacheType::Hash, false) => HashCache::new(cache_file.to_path_buf()).mark_failure()?,
(CacheType::TwoLayer, true) => {
TwoLayerCache::new(cache_file.to_path_buf()).mark_success()?
}
(CacheType::TwoLayer, false) => {
TwoLayerCache::new(cache_file.to_path_buf()).mark_failure()?
}
}
let label = if success { "success" } else { "failure" };
eprintln!("marked {label}");
Ok(ExitCode::SUCCESS)
}
fn run_invalidate(cache_file: &Path, cache_type: &CacheType) -> Result<ExitCode, FingerprintError> {
match cache_type {
CacheType::Hash => HashCache::new(cache_file.to_path_buf()).invalidate()?,
CacheType::TwoLayer => TwoLayerCache::new(cache_file.to_path_buf()).invalidate()?,
}
eprintln!("cache invalidated");
Ok(ExitCode::SUCCESS)
}