Skip to content

Commit c1acb50

Browse files
committed
feature: Add cache for remote loaded files
1 parent af498d0 commit c1acb50

File tree

11 files changed

+280
-24
lines changed

11 files changed

+280
-24
lines changed

Cargo.lock

Lines changed: 85 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

cli/Cargo.toml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ devrc-plugins = { path = "../plugins", version = "0.5.4"}
5353
devrc-core = { path = "../core", version = "0.5.4"}
5454
netrc-rs = "0.1.2"
5555
base64 = "0.21.2"
56+
duration-str = "0.5.1"
5657

5758
[build-dependencies]
5859
datetime = { version = "0.5.2", default_features = false }

cli/src/cache.rs

Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
use std::{
2+
fs::{self, File},
3+
io::Write,
4+
path::{Path, PathBuf},
5+
time::{Duration, SystemTime, UNIX_EPOCH},
6+
};
7+
8+
use sha256::digest;
9+
use url::Url;
10+
11+
use crate::{errors::DevrcResult, loader::LoadingConfig};
12+
13+
const DEVRC_CACHE_DIR_NAME: &str = "devrc";
14+
15+
/// Get absolute path to devrc cache dir
16+
pub fn get_cache_path() -> Option<PathBuf> {
17+
dirs_next::cache_dir().map(|path| Path::new(&path).join(DEVRC_CACHE_DIR_NAME))
18+
}
19+
20+
pub fn get_file_cache_meta(url: &Url) -> Option<PathBuf> {
21+
let hash = digest(url.as_str());
22+
get_cache_path().map(|path| path.join(format!("{:}.cache", hash)))
23+
}
24+
25+
#[derive(Debug, Default)]
26+
pub struct Cache {}
27+
28+
pub fn save(url: &Url, content: &str) -> DevrcResult<()> {
29+
if let Some(file) = get_file_cache_meta(url) {
30+
if let Some(dir) = file.parent() {
31+
if !dir.exists() {
32+
fs::create_dir_all(dir)?;
33+
}
34+
}
35+
36+
let mut f = File::create(file)?;
37+
f.write_all(content.as_bytes())?;
38+
}
39+
Ok(())
40+
}
41+
42+
pub fn load(
43+
url: &Url,
44+
_loading_config: &LoadingConfig,
45+
_checksum: Option<&str>,
46+
ttl: &Duration,
47+
) -> Option<String> {
48+
if let Some(file) = get_file_cache_meta(url) {
49+
if !file.exists() {
50+
return None;
51+
}
52+
53+
if let Ok(modified) = fs::metadata(&file).ok()?.modified() {
54+
let now_timestamp = SystemTime::now().duration_since(UNIX_EPOCH).ok()?;
55+
let created_timestamp = modified.duration_since(UNIX_EPOCH).ok()?;
56+
57+
let duration = now_timestamp - created_timestamp;
58+
59+
if duration < *ttl {
60+
return fs::read_to_string(file).ok();
61+
}
62+
}
63+
}
64+
None
65+
}

cli/src/config.rs

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use crate::interpreter::InterpreterKind;
2-
use std::{env, fmt::Debug, path::PathBuf};
2+
use std::{env, fmt::Debug, path::PathBuf, time::Duration};
33

44
use devrc_core::logging::LogLevel;
55

@@ -11,6 +11,7 @@ pub struct Config {
1111
pub dry_run: bool,
1212
pub default: Vec<String>,
1313
pub plugins: indexmap::IndexMap<String, PathBuf>,
14+
pub cache_ttl: Option<Duration>,
1415
}
1516

1617
impl Default for Config {
@@ -22,6 +23,7 @@ impl Default for Config {
2223
log_level: LogLevel::Info,
2324
default: vec![],
2425
plugins: indexmap::IndexMap::new(),
26+
cache_ttl: None,
2527
}
2628
}
2729
}

cli/src/devrcfile.rs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -118,6 +118,10 @@ impl Devrcfile {
118118
self.config.plugins.insert(name, path);
119119
}
120120
}
121+
122+
if let Some(duration) = config.cache_ttl {
123+
self.config.cache_ttl = Some(duration)
124+
}
121125
}
122126

123127
Ok(())

cli/src/env_file.rs

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,9 @@ pub struct UrlImport {
4040
pub ignore_errors: bool,
4141

4242
pub checksum: String,
43+
44+
#[serde(default)]
45+
pub headers: indexmap::IndexMap<String, String>,
4346
}
4447

4548
#[derive(Debug, Deserialize, Clone, Default)]
@@ -129,6 +132,16 @@ impl LocalFileImport {
129132
match loading_location {
130133
Location::LocalFile(path) => fs::read_to_string(path).map_err(DevrcError::IoError),
131134
Location::Remote { url, auth } => {
135+
if let Some(cache_ttl) = config.cache_ttl {
136+
if let Some(content) = crate::cache::load(&url, &config, None, &cache_ttl) {
137+
config.log_level.debug(
138+
&format!("\n==> Loading ENV URL CACHE: `{}` ...", &url),
139+
&config.designer.banner(),
140+
);
141+
return Ok(content);
142+
}
143+
}
144+
132145
let client = reqwest::blocking::Client::new();
133146
let mut headers_map: HeaderMap = HeaderMap::new();
134147

@@ -261,6 +274,18 @@ impl UrlImport {
261274
let parsed_url =
262275
Url::parse(&self.url).map_err(|_| DevrcError::InvalidIncludeUrl(self.url.clone()))?;
263276

277+
if let Some(cache_ttl) = config.cache_ttl {
278+
if let Some(content) =
279+
crate::cache::load(&parsed_url, &config, Some(&self.checksum), &cache_ttl)
280+
{
281+
config.log_level.debug(
282+
&format!("\n==> Loading ENV URL CACHE: `{}` ...", &parsed_url),
283+
&config.designer.banner(),
284+
);
285+
return Ok(content);
286+
}
287+
}
288+
264289
config.log_level.debug(
265290
&format!("\n==> Loading ENV FILE: `{:}` ...", &parsed_url),
266291
&config.designer.banner(),
@@ -280,6 +305,10 @@ impl UrlImport {
280305
});
281306
}
282307

308+
if config.cache_ttl.is_some() {
309+
crate::cache::save(&parsed_url, &content)?;
310+
}
311+
283312
Ok(content)
284313
}
285314
Ok(response) => {

cli/src/lib.rs

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@
1818
extern crate log;
1919

2020
pub mod auth;
21+
pub mod cache;
2122
pub mod cli;
2223
pub mod common;
2324
pub mod config;

cli/src/loader.rs

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,5 @@
1+
use std::time::Duration;
2+
13
use devrc_core::{logging::LogLevel, workshop::Designer};
24

35
#[derive(Debug, Clone)]
@@ -8,6 +10,8 @@ pub struct LoadingConfig {
810
pub(crate) log_level: LogLevel,
911

1012
pub designer: Designer,
13+
14+
pub cache_ttl: Option<Duration>,
1115
}
1216

1317
impl Default for LoadingConfig {
@@ -16,6 +20,7 @@ impl Default for LoadingConfig {
1620
level: 1,
1721
log_level: Default::default(),
1822
designer: Designer::default(),
23+
cache_ttl: None,
1924
}
2025
}
2126
}
@@ -26,6 +31,7 @@ impl LoadingConfig {
2631
level: 1,
2732
log_level,
2833
designer: Designer::default(),
34+
cache_ttl: None,
2935
}
3036
}
3137

@@ -42,4 +48,10 @@ impl LoadingConfig {
4248
..self
4349
}
4450
}
51+
pub fn with_cache_ttl(self, ttl: Option<Duration>) -> Self {
52+
Self {
53+
cache_ttl: ttl,
54+
..self
55+
}
56+
}
4557
}

cli/src/raw/config.rs

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
use crate::{de::deserialize_some, interpreter::InterpreterKind};
2-
use std::{fmt::Debug, path::PathBuf};
2+
use duration_str::deserialize_option_duration;
3+
use std::{fmt::Debug, path::PathBuf, time::Duration};
34

45
use devrc_core::logging;
56
use serde::Deserialize;
@@ -34,6 +35,9 @@ pub struct RawConfig {
3435

3536
#[serde(default, deserialize_with = "deserialize_some")]
3637
pub plugins: Option<indexmap::IndexMap<String, PathBuf>>,
38+
39+
#[serde(default, deserialize_with = "deserialize_option_duration")]
40+
pub cache_ttl: Option<Duration>,
3741
}
3842

3943
#[derive(Debug, Deserialize, Clone, PartialEq, Eq, PartialOrd, Ord, Default)]

0 commit comments

Comments
 (0)