Skip to content

Commit e4456ee

Browse files
committed
introducing cache trait
1 parent 1295e91 commit e4456ee

File tree

7 files changed

+165
-131
lines changed

7 files changed

+165
-131
lines changed

src/cache/file.rs

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
use crate::project::Error;
2+
use error_stack::{Result, ResultExt};
3+
use std::{
4+
collections::HashMap,
5+
fs::{self, File, OpenOptions},
6+
io::{BufReader, BufWriter},
7+
path::{Path, PathBuf},
8+
sync::Mutex,
9+
};
10+
11+
use super::{Cache, FileOwnerCacheEntry};
12+
13+
#[derive(Debug)]
14+
pub struct GlobalCache {
15+
base_path: PathBuf,
16+
cache_directory: String,
17+
file_owner_cache: Option<Box<Mutex<HashMap<PathBuf, FileOwnerCacheEntry>>>>,
18+
}
19+
20+
const DEFAULT_CACHE_CAPACITY: usize = 10000;
21+
22+
impl Cache for GlobalCache {
23+
fn get_file_owner(&self, path: &Path) -> Result<Option<FileOwnerCacheEntry>, Error> {
24+
if let Ok(cache) = self.file_owner_cache.as_ref().unwrap().lock() {
25+
if let Some(cached_entry) = cache.get(path) {
26+
let timestamp = Self::get_file_timestamp(path)?;
27+
if cached_entry.timestamp == timestamp {
28+
return Ok(Some(cached_entry.clone()));
29+
}
30+
}
31+
}
32+
Ok(None)
33+
}
34+
35+
fn write_file_owner(&self, path: &Path, owner: Option<String>) {
36+
if let Ok(mut cache) = self.file_owner_cache.as_ref().unwrap().lock() {
37+
if let Ok(timestamp) = Self::get_file_timestamp(path) {
38+
cache.insert(path.to_path_buf(), FileOwnerCacheEntry { timestamp, owner });
39+
}
40+
}
41+
}
42+
43+
fn persist_cache(&self) -> Result<(), Error> {
44+
let cache_path = self.get_cache_path();
45+
let file = OpenOptions::new()
46+
.write(true)
47+
.create(true)
48+
.truncate(true)
49+
.open(cache_path)
50+
.change_context(Error::Io)?;
51+
52+
let writer = BufWriter::new(file);
53+
let cache = self.file_owner_cache.as_ref().unwrap().lock().map_err(|_| Error::Io)?;
54+
serde_json::to_writer(writer, &*cache).change_context(Error::SerdeJson)
55+
}
56+
57+
fn delete_cache(&self) -> Result<(), Error> {
58+
let cache_path = self.get_cache_path();
59+
dbg!("deleting", &cache_path);
60+
fs::remove_file(cache_path).change_context(Error::Io)
61+
}
62+
}
63+
64+
impl GlobalCache {
65+
pub fn new(base_path: PathBuf, cache_directory: String) -> Result<Self, Error> {
66+
let mut cache = Self {
67+
base_path,
68+
cache_directory,
69+
file_owner_cache: None,
70+
};
71+
cache.load_cache().change_context(Error::Io)?;
72+
Ok(cache)
73+
}
74+
75+
fn load_cache(&mut self) -> Result<(), Error> {
76+
let cache_path = self.get_cache_path();
77+
if !cache_path.exists() {
78+
self.file_owner_cache = Some(Box::new(Mutex::new(HashMap::with_capacity(DEFAULT_CACHE_CAPACITY))));
79+
return Ok(());
80+
}
81+
82+
let file = File::open(cache_path).change_context(Error::Io)?;
83+
let reader = BufReader::new(file);
84+
let json = serde_json::from_reader(reader);
85+
self.file_owner_cache = match json {
86+
Ok(cache) => Some(Box::new(Mutex::new(cache))),
87+
_ => Some(Box::new(Mutex::new(HashMap::with_capacity(DEFAULT_CACHE_CAPACITY)))),
88+
};
89+
Ok(())
90+
}
91+
92+
fn get_cache_path(&self) -> PathBuf {
93+
let cache_dir = self.base_path.join(PathBuf::from(&self.cache_directory));
94+
fs::create_dir_all(&cache_dir).unwrap();
95+
96+
cache_dir.join("project-file-cache.json")
97+
}
98+
99+
fn get_file_timestamp(path: &Path) -> Result<u64, Error> {
100+
let metadata = fs::metadata(path).change_context(Error::Io)?;
101+
metadata
102+
.modified()
103+
.change_context(Error::Io)?
104+
.duration_since(std::time::UNIX_EPOCH)
105+
.change_context(Error::Io)
106+
.map(|duration| duration.as_secs())
107+
}
108+
}

src/cache/mod.rs

Lines changed: 7 additions & 101 deletions
Original file line numberDiff line numberDiff line change
@@ -1,113 +1,19 @@
11
use crate::project::Error;
2-
use error_stack::{Result, ResultExt};
3-
use std::{
4-
collections::HashMap,
5-
fs::{self, File, OpenOptions},
6-
io::{BufReader, BufWriter},
7-
path::{Path, PathBuf},
8-
sync::Mutex,
9-
};
2+
use error_stack::Result;
3+
use std::path::Path;
4+
5+
pub mod file;
6+
pub mod noop;
107

118
pub trait Cache {
129
fn get_file_owner(&self, path: &Path) -> Result<Option<FileOwnerCacheEntry>, Error>;
1310
fn write_file_owner(&self, path: &Path, owner: Option<String>);
14-
}
15-
16-
#[derive(Debug)]
17-
pub struct GlobalCache<'a> {
18-
base_path: &'a PathBuf,
19-
cache_directory: &'a String,
20-
file_owner_cache: Option<Box<Mutex<HashMap<PathBuf, FileOwnerCacheEntry>>>>,
11+
fn persist_cache(&self) -> Result<(), Error>;
12+
fn delete_cache(&self) -> Result<(), Error>;
2113
}
2214

2315
#[derive(Clone, Debug, serde::Serialize, serde::Deserialize)]
2416
pub struct FileOwnerCacheEntry {
2517
timestamp: u64,
2618
pub owner: Option<String>,
2719
}
28-
29-
const DEFAULT_CACHE_CAPACITY: usize = 10000;
30-
31-
impl<'a> GlobalCache<'a> {
32-
pub fn new(base_path: &'a PathBuf, cache_directory: &'a String) -> Self {
33-
Self {
34-
base_path,
35-
cache_directory,
36-
file_owner_cache: None,
37-
}
38-
}
39-
40-
pub fn persist_cache(&self) -> Result<(), Error> {
41-
let cache_path = self.get_cache_path();
42-
let file = OpenOptions::new()
43-
.write(true)
44-
.create(true)
45-
.truncate(true)
46-
.open(cache_path)
47-
.change_context(Error::Io)?;
48-
49-
let writer = BufWriter::new(file);
50-
let cache = self.file_owner_cache.as_ref().unwrap().lock().map_err(|_| Error::Io)?;
51-
serde_json::to_writer(writer, &*cache).change_context(Error::SerdeJson)
52-
}
53-
54-
pub fn load_cache(&mut self) -> Result<(), Error> {
55-
let cache_path = self.get_cache_path();
56-
if !cache_path.exists() {
57-
self.file_owner_cache = Some(Box::new(Mutex::new(HashMap::with_capacity(DEFAULT_CACHE_CAPACITY))));
58-
return Ok(());
59-
}
60-
61-
let file = File::open(cache_path).change_context(Error::Io)?;
62-
let reader = BufReader::new(file);
63-
let json = serde_json::from_reader(reader);
64-
self.file_owner_cache = match json {
65-
Ok(cache) => Some(Box::new(Mutex::new(cache))),
66-
_ => Some(Box::new(Mutex::new(HashMap::with_capacity(DEFAULT_CACHE_CAPACITY)))),
67-
};
68-
Ok(())
69-
}
70-
71-
pub fn get_file_owner(&self, path: &Path) -> Result<Option<FileOwnerCacheEntry>, Error> {
72-
if let Ok(cache) = self.file_owner_cache.as_ref().unwrap().lock() {
73-
if let Some(cached_entry) = cache.get(path) {
74-
let timestamp = Self::get_file_timestamp(path)?;
75-
if cached_entry.timestamp == timestamp {
76-
return Ok(Some(cached_entry.clone()));
77-
}
78-
}
79-
}
80-
Ok(None)
81-
}
82-
83-
pub fn write_file_owner(&self, path: &Path, owner: Option<String>) {
84-
if let Ok(mut cache) = self.file_owner_cache.as_ref().unwrap().lock() {
85-
if let Ok(timestamp) = Self::get_file_timestamp(path) {
86-
cache.insert(path.to_path_buf(), FileOwnerCacheEntry { timestamp, owner });
87-
}
88-
}
89-
}
90-
91-
fn get_cache_path(&self) -> PathBuf {
92-
let cache_dir = self.base_path.join(PathBuf::from(&self.cache_directory));
93-
fs::create_dir_all(&cache_dir).unwrap();
94-
95-
cache_dir.join("project-file-cache.json")
96-
}
97-
98-
pub fn delete_cache(&self) -> Result<(), Error> {
99-
let cache_path = self.get_cache_path();
100-
dbg!("deleting", &cache_path);
101-
fs::remove_file(cache_path).change_context(Error::Io)
102-
}
103-
104-
fn get_file_timestamp(path: &Path) -> Result<u64, Error> {
105-
let metadata = fs::metadata(path).change_context(Error::Io)?;
106-
metadata
107-
.modified()
108-
.change_context(Error::Io)?
109-
.duration_since(std::time::UNIX_EPOCH)
110-
.change_context(Error::Io)
111-
.map(|duration| duration.as_secs())
112-
}
113-
}

src/cache/noop.rs

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
use crate::project::Error;
2+
use error_stack::Result;
3+
use std::path::Path;
4+
5+
use super::{Cache, FileOwnerCacheEntry};
6+
7+
#[derive(Default)]
8+
pub struct NoopCache {}
9+
10+
impl Cache for NoopCache {
11+
fn get_file_owner(&self, _path: &Path) -> Result<Option<FileOwnerCacheEntry>, Error> {
12+
Ok(None)
13+
}
14+
15+
fn write_file_owner(&self, _path: &Path, _owner: Option<String>) {
16+
// noop
17+
}
18+
19+
fn persist_cache(&self) -> Result<(), Error> {
20+
Ok(())
21+
}
22+
23+
fn delete_cache(&self) -> Result<(), Error> {
24+
Ok(())
25+
}
26+
}

src/cli.rs

Lines changed: 9 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
use clap::{Parser, Subcommand};
22
use codeowners::{
3-
cache::GlobalCache,
3+
cache::{file::GlobalCache, noop::NoopCache, Cache},
44
config::Config,
55
ownership::{FileOwner, Ownership},
66
project_builder::ProjectBuilder,
@@ -102,20 +102,17 @@ pub fn cli() -> Result<(), Error> {
102102

103103
let config: Config = serde_yaml::from_reader(config_file).change_context(Error::Io)?;
104104

105-
let mut global_cache = GlobalCache::new(&project_root, &config.cache_directory);
106-
global_cache.load_cache().change_context(Error::Io)?;
105+
let cache: &dyn Cache = if args.no_cache {
106+
&NoopCache::default() as &dyn Cache
107+
} else {
108+
&GlobalCache::new(project_root.clone(), config.cache_directory.clone()).change_context(Error::Io)? as &dyn Cache
109+
};
107110

108-
let mut project_builder = ProjectBuilder::new(
109-
&config,
110-
project_root.clone(),
111-
codeowners_file_path.clone(),
112-
!args.no_cache,
113-
&global_cache,
114-
);
111+
let mut project_builder = ProjectBuilder::new(&config, project_root.clone(), codeowners_file_path.clone(), !args.no_cache, cache);
115112
let project = project_builder.build().change_context(Error::Io)?;
116113
let ownership = Ownership::build(project);
117114

118-
global_cache.persist_cache().change_context(Error::Io)?;
115+
cache.persist_cache().change_context(Error::Io)?;
119116

120117
match args.command {
121118
Command::Validate => ownership.validate().change_context(Error::ValidationFailed)?,
@@ -153,7 +150,7 @@ pub fn cli() -> Result<(), Error> {
153150
Err(err) => println!("{}", err),
154151
},
155152
Command::DeleteCache => {
156-
global_cache.delete_cache().change_context(Error::Io)?;
153+
cache.delete_cache().change_context(Error::Io)?;
157154
}
158155
}
159156

src/common_test.rs

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,12 @@ pub mod tests {
1010

1111
use tempfile::tempdir;
1212

13-
use crate::{cache::GlobalCache, config::Config, ownership::Ownership, project_builder::ProjectBuilder};
13+
use crate::{
14+
cache::{noop::NoopCache, Cache},
15+
config::Config,
16+
ownership::Ownership,
17+
project_builder::ProjectBuilder,
18+
};
1419

1520
macro_rules! ownership {
1621
($($test_files:expr),+) => {{
@@ -110,13 +115,13 @@ pub mod tests {
110115
let config: Config = serde_yaml::from_reader(config_file)?;
111116

112117
let codeowners_file_path = &test_config.temp_dir_path.join(".github/CODEOWNERS");
113-
let global_cache = GlobalCache::new(&test_config.temp_dir_path, &config.cache_directory);
118+
let cache: &dyn Cache = &NoopCache::default();
114119
let mut builder = ProjectBuilder::new(
115120
&config,
116121
test_config.temp_dir_path.clone(),
117122
codeowners_file_path.clone(),
118123
false,
119-
&global_cache,
124+
cache,
120125
);
121126
let project = builder.build()?;
122127
let ownership = Ownership::build(project);
@@ -129,7 +134,7 @@ pub mod tests {
129134
test_config.temp_dir_path.clone(),
130135
codeowners_file_path.clone(),
131136
false,
132-
&global_cache,
137+
cache,
133138
);
134139
let project = builder.build()?;
135140
Ok(Ownership::build(project))

src/project_builder.rs

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use rayon::iter::{IntoParallelIterator, ParallelIterator};
1010
use tracing::instrument;
1111

1212
use crate::{
13-
cache::GlobalCache,
13+
cache::Cache,
1414
config::Config,
1515
project::{deserializers, DirectoryCodeownersFile, Error, Package, PackageType, Project, ProjectFile, Team, VendoredGem},
1616
project_file_builder::ProjectFileBuilder,
@@ -28,7 +28,6 @@ enum EntryType {
2828
NullEntry(),
2929
}
3030

31-
#[derive(Debug)]
3231
pub struct ProjectBuilder<'a> {
3332
config: &'a Config,
3433
base_path: PathBuf,
@@ -39,14 +38,8 @@ pub struct ProjectBuilder<'a> {
3938
const INITIAL_VECTOR_CAPACITY: usize = 1000;
4039

4140
impl<'a> ProjectBuilder<'a> {
42-
pub fn new(
43-
config: &'a Config,
44-
base_path: PathBuf,
45-
codeowners_file_path: PathBuf,
46-
use_cache: bool,
47-
global_cache: &'a GlobalCache,
48-
) -> Self {
49-
let project_file_builder = ProjectFileBuilder::new(use_cache, global_cache);
41+
pub fn new(config: &'a Config, base_path: PathBuf, codeowners_file_path: PathBuf, use_cache: bool, cache: &'a dyn Cache) -> Self {
42+
let project_file_builder = ProjectFileBuilder::new(use_cache, cache);
5043
Self {
5144
project_file_builder,
5245
config,

0 commit comments

Comments
 (0)