use anyhow::{Context, Result};
use log::info;
use std::path::{Path, PathBuf};
use tokio::fs::{self, File, create_dir_all};
use tokio::io::AsyncWriteExt;
use crate::model::LanguageScript;
pub struct CodeFileCache;
impl CodeFileCache {
pub fn obtain_code_hash(code: &str) -> String {
let hash = blake3::hash(code.as_bytes());
let hash_str = hash.to_hex().to_string();
info!("计算代码hash值: {}", hash_str);
hash_str
}
pub async fn check_code_file_cache_exisht(hash: &str, language: &LanguageScript) -> bool {
fs::try_exists(Self::get_cache_file_path(hash, language))
.await
.unwrap_or(false)
}
pub async fn get_code_file_cache(
hash: &str,
language: &LanguageScript,
) -> Result<(File, PathBuf)> {
let file_path = Self::get_cache_file_path(hash, language);
let file = File::open(&file_path)
.await
.with_context(|| format!("无法打开缓存文件: {}", file_path.display()))?;
Ok((file, file_path))
}
pub async fn save_code_file_cache(
hash: &str,
code: &str,
language: &LanguageScript,
) -> Result<(File, PathBuf)> {
let cache_dir = Self::get_cache_dir();
if !fs::try_exists(&cache_dir).await.unwrap_or(false) {
create_dir_all(&cache_dir)
.await
.with_context(|| format!("无法创建缓存目录: {}", cache_dir.display()))?;
}
let file_path = Self::get_cache_file_path(hash, language);
let mut file = File::create(&file_path)
.await
.with_context(|| format!("无法创建缓存文件: {}", file_path.display()))?;
file.write_all(code.as_bytes())
.await
.with_context(|| "写入代码到缓存文件失败")?;
#[cfg(unix)]
{
use std::os::unix::fs::PermissionsExt;
let mut perms = fs::metadata(&file_path).await?.permissions();
perms.set_mode(0o755); fs::set_permissions(&file_path, perms).await?;
}
let file_path = Self::get_cache_file_path(hash, language);
Ok((file, file_path))
}
pub async fn clear_cache_by_language(language: &LanguageScript) -> Result<()> {
let cache_dir = Self::get_cache_dir();
if !fs::try_exists(&cache_dir).await.unwrap_or(false) {
return Ok(());
}
let suffix = language.get_file_suffix();
let mut entries = fs::read_dir(cache_dir).await?;
while let Some(entry) = entries.next_entry().await? {
let path = entry.path();
if let Some(file_name) = path.file_name()
&& let Some(name_str) = file_name.to_str()
&& name_str.ends_with(suffix)
{
fs::remove_file(&path)
.await
.with_context(|| format!("无法删除缓存文件: {}", path.display()))?;
}
}
Ok(())
}
pub async fn clear_all_cache() -> Result<()> {
let cache_dir = Self::get_cache_dir();
if fs::try_exists(&cache_dir).await.unwrap_or(false) {
fs::remove_dir_all(&cache_dir)
.await
.with_context(|| format!("无法删除缓存目录: {}", cache_dir.display()))?;
}
Ok(())
}
fn get_cache_dir() -> PathBuf {
Path::new("/tmp/code_cache").to_path_buf()
}
fn get_cache_file_path(hash: &str, language: &LanguageScript) -> PathBuf {
let mut path = Self::get_cache_dir();
let file_name = format!("{}{}", hash, language.get_file_suffix());
path.push(file_name);
path
}
}