Skip to content

Commit

Permalink
feat: basic caching support (#25)
Browse files Browse the repository at this point in the history
  • Loading branch information
uncenter authored Jun 29, 2024
1 parent b377a00 commit f109a65
Show file tree
Hide file tree
Showing 8 changed files with 180 additions and 33 deletions.
21 changes: 21 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ color-eyre = "0.6.3"
convert_case = "0.6.0"
css-colors = "1.0.1"
csscolorparser = { version = "0.6.2", features = ["rgb"] }
etcetera = "0.8.0"
fancy-regex = "0.13.0"
flate2 = "1.0.30"
graphql_client = { version = "0.14.0", features = ["reqwest-blocking"] }
Expand Down
91 changes: 91 additions & 0 deletions src/cache.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
use color_eyre::eyre::Result;
use serde::{Deserialize, Serialize};
use std::{
collections::HashMap,
fs,
io::{self, Write},
path::PathBuf,
time::SystemTime,
};

static ONE_DAY_IN_SECONDS: u64 = 24 * 60 * 60;

#[derive(Serialize, Deserialize, Debug)]
pub struct Entry {
timestamp: SystemTime,
data: String,
}

#[derive(Serialize, Deserialize, Debug, Default)]
pub struct Cache {
path: PathBuf,
entries: HashMap<String, Entry>,
refresh: bool,
}

impl Cache {
pub fn new(path: PathBuf, refresh: bool) -> Self {
let entries = match fs::read_to_string(&path) {
Ok(contents) => serde_json::from_str(&contents).unwrap_or_default(),
Err(_) => {
fs::create_dir_all(path.parent().unwrap()).unwrap();
HashMap::new()
}
};

Cache {
path,
entries,
refresh,
}
}

pub fn get(&self, key: &str) -> Option<&String> {
if self.refresh {
return None;
}
match self.entries.get(key) {
Some(entry) => {
let diff = SystemTime::now()
.duration_since(entry.timestamp)
.unwrap()
.as_secs();
if diff < ONE_DAY_IN_SECONDS {
Some(&entry.data)
} else {
None
}
}
None => None,
}
}

pub fn get_or<F>(&mut self, key: &str, fetch: F) -> Result<String>
where
F: FnOnce() -> Result<String>,
{
if let Some(data) = self.get(key) {
return Ok(data.clone());
}
let value = fetch()?;
self.save(key, value.clone())?;
Ok(value)
}

pub fn save(&mut self, key: &str, value: String) -> Result<String> {
self.entries.insert(
key.to_string(),
Entry {
timestamp: SystemTime::now(),
data: value.clone(),
},
);
self.save_to_file()?;
Ok(value)
}

fn save_to_file(&self) -> io::Result<()> {
let mut file = fs::File::create(&self.path)?;
file.write_all(serde_json::to_string(&self.entries)?.as_bytes())
}
}
4 changes: 4 additions & 0 deletions src/cli.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ use crate::models::shared::CATEGORIES;
pub struct Cli {
#[command(subcommand)]
pub command: Commands,

/// Hard refresh cached data
#[arg(short, long, global = true)]
pub refresh: bool,
}

#[derive(Subcommand)]
Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use models::{
userstyles::Userstyle,
};

pub mod cache;
pub mod cli;
pub mod github;
pub mod models;
Expand Down Expand Up @@ -132,3 +133,9 @@ pub fn get_userstyle_key(entry: (String, Userstyle), key: UserstyleKey) -> Value
),
}
}

fn fetch_text(url: &str) -> Result<String> {
let response = reqwest::blocking::get(url)?;
let text = response.text()?;
Ok(text)
}
18 changes: 14 additions & 4 deletions src/main.rs
Original file line number Diff line number Diff line change
@@ -1,40 +1,50 @@
use catppuccin_purr::{
cache::Cache,
cli::{Cli, Commands, Userstyles},
ports, userstyles, whiskerify,
};
use clap::Parser;
use color_eyre::eyre::Result;
use etcetera::{choose_base_strategy, BaseStrategy};

fn main() -> Result<()> {
color_eyre::install()?;
pretty_env_logger::formatted_builder()
.filter_level(log::LevelFilter::Warn)
.init();

let args = Cli::parse();
let args: Cli = Cli::parse();

let cache = Cache::new(
choose_base_strategy()
.unwrap()
.cache_dir()
.join("purr/store.json"),
args.refresh,
);

match args.command {
Commands::Query {
command,
r#for,
count,
get,
} => ports::query(command, r#for, count, get)?,
} => ports::query(cache, command, r#for, count, get)?,
Commands::Init { name, url } => ports::init(name, url)?,
Commands::Userstyles { command } => match command {
Userstyles::Query {
command,
r#for,
count,
get,
} => userstyles::query(command, r#for, count, get)?,
} => userstyles::query(cache, command, r#for, count, get)?,
Userstyles::Init {
name,
categories,
icon,
color,
url,
} => userstyles::init(name, categories, icon, color, url)?,
} => userstyles::init(cache, name, categories, icon, color, url)?,
},
Commands::Whiskerify { input, output } => whiskerify::handle(input, output)?,
}
Expand Down
22 changes: 15 additions & 7 deletions src/ports.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,20 +7,28 @@ use inquire::Text;
use serde_json::Value;
use url::Url;

use crate::cache::Cache;
use crate::cli::{Key, Query, WhiskersCustomProperty};
use crate::github::{
self, fetch_all_repositories, fetch_whiskers_custom_property, RepositoryResponse,
};
use crate::models::ports::Root;
use crate::models::shared::StringOrStrings;
use crate::{display_json_or_count, get_key, is_booleanish_match, matches_current_maintainer};
use crate::{
display_json_or_count, fetch_text, get_key, is_booleanish_match, matches_current_maintainer,
};

pub fn query(command: Option<Query>, r#for: Option<String>, count: bool, get: Key) -> Result<()> {
let raw: String = reqwest::blocking::get(
"https://github.com/catppuccin/catppuccin/raw/main/resources/ports.yml",
)?
.text()?;
let data: Root = serde_yaml::from_str(&raw).unwrap();
pub fn query(
mut cache: Cache,
command: Option<Query>,
r#for: Option<String>,
count: bool,
get: Key,
) -> Result<()> {
let ports = cache.get_or("ports-yml", || {
fetch_text("https://github.com/catppuccin/catppuccin/raw/main/resources/ports.yml")
})?;
let data: Root = serde_yaml::from_str(&ports).unwrap();

match command {
Some(Query::Maintained { by, options }) => {
Expand Down
49 changes: 27 additions & 22 deletions src/userstyles.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,24 +7,26 @@ use inquire::validator::Validation;
use inquire::{MultiSelect, Select, Text};
use url::Url;

use crate::cache::Cache;
use crate::cli::{UserstyleKey, UserstylesQuery};
use crate::models::shared::{StringOrStrings, CATEGORIES};
use crate::models::userstyles::{Readme, Root, Userstyle, UserstylesRoot};
use crate::{
display_json_or_count, get_userstyle_key, is_booleanish_match, matches_current_maintainer,
display_json_or_count, fetch_text, get_userstyle_key, is_booleanish_match,
matches_current_maintainer,
};

pub fn query(
mut cache: Cache,
command: Option<UserstylesQuery>,
r#for: Option<String>,
count: bool,
get: UserstyleKey,
) -> Result<()> {
let raw: String = reqwest::blocking::get(
"https://github.com/catppuccin/userstyles/raw/main/scripts/userstyles.yml",
)?
.text()?;
let data: Root = serde_yaml::from_str(&raw).unwrap();
let userstyles = cache.get_or("userstyles-yml", || {
fetch_text("https://github.com/catppuccin/userstyles/raw/main/scripts/userstyles.yml")
})?;
let data: Root = serde_yaml::from_str(&userstyles).unwrap();

match command {
Some(UserstylesQuery::Maintained { by, options }) => {
Expand Down Expand Up @@ -147,6 +149,7 @@ pub fn query(
}

pub fn init(
mut cache: Cache,
name: Option<String>,
categories: Option<Vec<String>>,
icon: Option<String>,
Expand Down Expand Up @@ -215,22 +218,24 @@ pub fn init(
}
fs::create_dir(&target)?;

let mut template: String = reqwest::blocking::get(
"https://github.com/catppuccin/userstyles/raw/main/template/catppuccin.user.css",
)?
.text()?
.replace("<port-name> Catppuccin", &format!("{} Catppuccin", &name))
.replace(
"Soothing pastel theme for <port-name>",
&format!("Soothing pastel theme for {}", &name),
)
.replace("<port-name>", &name_kebab)
.replace(
"<website-domain>",
Url::parse(&url)?
.host_str()
.expect("App link should be a valid URL"),
);
let mut template = cache
.get_or("userstyles-template", || {
fetch_text(
"https://github.com/catppuccin/userstyles/raw/main/template/catppuccin.user.css",
)
})?
.replace("<port-name> Catppuccin", &format!("{} Catppuccin", &name))
.replace(
"Soothing pastel theme for <port-name>",
&format!("Soothing pastel theme for {}", &name),
)
.replace("<port-name>", &name_kebab)
.replace(
"<website-domain>",
Url::parse(&url)?
.host_str()
.expect("App link should be a valid URL"),
);

let comment_re =
fancy_regex::Regex::new(r"\/\*(?:(?!\*\/|==UserStyle==|prettier-ignore)[\s\S])*?\*\/")?;
Expand Down

0 comments on commit f109a65

Please sign in to comment.