mirror of https://github.com/astral-sh/ruff
185 lines
4.1 KiB
Rust
185 lines
4.1 KiB
Rust
use std::collections::hash_map::DefaultHasher;
|
|
use std::fs;
|
|
use std::fs::{create_dir_all, File, Metadata};
|
|
use std::hash::{Hash, Hasher};
|
|
use std::io::Write;
|
|
use std::path::Path;
|
|
|
|
use anyhow::Result;
|
|
use filetime::FileTime;
|
|
use log::error;
|
|
use path_absolutize::Absolutize;
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use crate::autofix::fixer;
|
|
use crate::message::Message;
|
|
use crate::settings::Settings;
|
|
|
|
const CARGO_PKG_VERSION: &str = env!("CARGO_PKG_VERSION");
|
|
|
|
#[derive(Serialize, Deserialize)]
|
|
struct CacheMetadata {
|
|
mtime: i64,
|
|
}
|
|
|
|
#[derive(Serialize)]
|
|
struct CheckResultRef<'a> {
|
|
metadata: &'a CacheMetadata,
|
|
messages: &'a [Message],
|
|
}
|
|
|
|
#[derive(Deserialize)]
|
|
struct CheckResult {
|
|
metadata: CacheMetadata,
|
|
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 {
|
|
if value {
|
|
Mode::ReadWrite
|
|
} else {
|
|
Mode::None
|
|
}
|
|
}
|
|
}
|
|
|
|
fn cache_dir() -> &'static str {
|
|
"./.ruff_cache"
|
|
}
|
|
|
|
fn content_dir() -> &'static str {
|
|
"content"
|
|
}
|
|
|
|
fn cache_key(path: &Path, settings: &Settings, autofix: &fixer::Mode) -> u64 {
|
|
let mut hasher = DefaultHasher::new();
|
|
CARGO_PKG_VERSION.hash(&mut hasher);
|
|
path.absolutize().unwrap().hash(&mut hasher);
|
|
settings.hash(&mut hasher);
|
|
autofix.hash(&mut hasher);
|
|
hasher.finish()
|
|
}
|
|
|
|
/// Initialize the cache directory.
|
|
pub fn init() -> Result<()> {
|
|
let path = Path::new(cache_dir());
|
|
|
|
// Create the cache directories.
|
|
create_dir_all(path.join(content_dir()))?;
|
|
|
|
// Add the CACHEDIR.TAG.
|
|
if !cachedir::is_tagged(path)? {
|
|
cachedir::add_tag(path)?;
|
|
}
|
|
|
|
// Add the .gitignore.
|
|
let gitignore_path = path.join(".gitignore");
|
|
if !gitignore_path.exists() {
|
|
let mut file = File::create(gitignore_path)?;
|
|
file.write_all(b"*")?;
|
|
}
|
|
|
|
Ok(())
|
|
}
|
|
|
|
fn write_sync(key: u64, value: &[u8]) -> Result<(), std::io::Error> {
|
|
fs::write(
|
|
Path::new(cache_dir())
|
|
.join(content_dir())
|
|
.join(format!("{key:x}")),
|
|
value,
|
|
)
|
|
}
|
|
|
|
fn read_sync(key: u64) -> Result<Vec<u8>, std::io::Error> {
|
|
fs::read(
|
|
Path::new(cache_dir())
|
|
.join(content_dir())
|
|
.join(format!("{key:x}")),
|
|
)
|
|
}
|
|
|
|
/// Get a value from the cache.
|
|
pub fn get(
|
|
path: &Path,
|
|
metadata: &Metadata,
|
|
settings: &Settings,
|
|
autofix: &fixer::Mode,
|
|
mode: &Mode,
|
|
) -> Option<Vec<Message>> {
|
|
if !mode.allow_read() {
|
|
return None;
|
|
};
|
|
|
|
let encoded = read_sync(cache_key(path, settings, autofix)).ok()?;
|
|
let (mtime, messages) = match bincode::deserialize::<CheckResult>(&encoded[..]) {
|
|
Ok(CheckResult {
|
|
metadata: CacheMetadata { mtime },
|
|
messages,
|
|
}) => (mtime, messages),
|
|
Err(e) => {
|
|
error!("Failed to deserialize encoded cache entry: {e:?}");
|
|
return None;
|
|
}
|
|
};
|
|
if FileTime::from_last_modification_time(metadata).unix_seconds() != mtime {
|
|
return None;
|
|
}
|
|
Some(messages)
|
|
}
|
|
|
|
/// Set a value in the cache.
|
|
pub fn set(
|
|
path: &Path,
|
|
metadata: &Metadata,
|
|
settings: &Settings,
|
|
autofix: &fixer::Mode,
|
|
messages: &[Message],
|
|
mode: &Mode,
|
|
) {
|
|
if !mode.allow_write() {
|
|
return;
|
|
};
|
|
|
|
let check_result = CheckResultRef {
|
|
metadata: &CacheMetadata {
|
|
mtime: FileTime::from_last_modification_time(metadata).unix_seconds(),
|
|
},
|
|
messages,
|
|
};
|
|
if let Err(e) = write_sync(
|
|
cache_key(path, settings, autofix),
|
|
&bincode::serialize(&check_result).unwrap(),
|
|
) {
|
|
error!("Failed to write to cache: {e:?}");
|
|
}
|
|
}
|