|
@ -6,15 +6,19 @@ use reqwest::header::{ACCEPT, CONTENT_TYPE, USER_AGENT}; |
|
|
use serde::{Deserialize, Serialize};
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
use std::fs::File;
|
|
|
use std::fs::File;
|
|
|
use std::io::prelude::*;
|
|
|
use std::io::prelude::*;
|
|
|
use std::io::BufReader;
|
|
|
|
|
|
|
|
|
use std::io::{BufReader, BufWriter};
|
|
|
use std::path::PathBuf;
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
use std::time::{Duration, SystemTime};
|
|
|
|
|
|
|
|
|
use crate::errors::*;
|
|
|
use crate::errors::*;
|
|
|
|
|
|
|
|
|
|
|
|
/// Default user agent string used for api requests
|
|
|
pub const DEFAULT_USER_AGENT: &str = "gitig";
|
|
|
pub const DEFAULT_USER_AGENT: &str = "gitig";
|
|
|
|
|
|
|
|
|
|
|
|
/// Response objects from github api
|
|
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
|
|
#[allow(clippy::missing_docs_in_private_items)]
|
|
|
pub struct Template {
|
|
|
pub struct Template {
|
|
|
pub name: String,
|
|
|
pub name: String,
|
|
|
pub path: String,
|
|
|
pub path: String,
|
|
@ -33,8 +37,10 @@ pub struct Template { |
|
|
pub links: Links,
|
|
|
pub links: Links,
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/// Part of github api response
|
|
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
|
|
|
#[allow(clippy::missing_docs_in_private_items)]
|
|
|
pub struct Links {
|
|
|
pub struct Links {
|
|
|
#[serde(rename = "self")]
|
|
|
#[serde(rename = "self")]
|
|
|
pub self_field: String,
|
|
|
pub self_field: String,
|
|
@ -76,14 +82,40 @@ impl GithubTemplates { |
|
|
.send()
|
|
|
.send()
|
|
|
.chain_err(|| "Error while sending request to query all templates")?;
|
|
|
.chain_err(|| "Error while sending request to query all templates")?;
|
|
|
let body: Vec<Template> = res.json().chain_err(|| "json")?;
|
|
|
let body: Vec<Template> = res.json().chain_err(|| "json")?;
|
|
|
|
|
|
debug!("Received and deserialized {} templates", body.len());
|
|
|
|
|
|
trace!("Serializing templates to file cache");
|
|
|
|
|
|
let cache = File::create(Self::cache_path())
|
|
|
|
|
|
.chain_err(|| "Error while creating file cache to write")?;
|
|
|
|
|
|
let writer = BufWriter::new(cache);
|
|
|
|
|
|
serde_json::to_writer(writer, &body)
|
|
|
|
|
|
.chain_err(|| "Error while serialzing templates to file cache")?;
|
|
|
|
|
|
debug!("Serialization of templates completed");
|
|
|
Ok(GithubTemplates { templates: body })
|
|
|
Ok(GithubTemplates { templates: body })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/// Returns whether there is an cached json version
|
|
|
/// Returns whether there is an cached json version
|
|
|
fn is_cached() -> bool { Self::cache_path().exists() }
|
|
|
|
|
|
|
|
|
fn is_cached() -> bool {
|
|
|
|
|
|
let path = Self::cache_path();
|
|
|
|
|
|
if !path.exists() {
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
let modified: SystemTime = path
|
|
|
|
|
|
.metadata()
|
|
|
|
|
|
.map(|meta| meta.modified().unwrap_or(SystemTime::UNIX_EPOCH))
|
|
|
|
|
|
.unwrap_or(SystemTime::UNIX_EPOCH);
|
|
|
|
|
|
|
|
|
|
|
|
let max_age = Duration::from_secs(60 * 24 * 2 /* two days */);
|
|
|
|
|
|
if modified.elapsed().unwrap_or(Duration::from_secs(u64::max_value())) > max_age {
|
|
|
|
|
|
debug!("Cache file is too older (> days), won't be used");
|
|
|
|
|
|
return false;
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
true
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
/// Returns the path of the file cache
|
|
|
/// Returns the path of the file cache
|
|
|
fn cache_path() -> PathBuf {
|
|
|
fn cache_path() -> PathBuf {
|
|
|
|
|
|
let proj = directories::ProjectDirs::from("org", "webschneider", env!("CARGO_PKG_NAME"));
|
|
|
let mut cache: PathBuf = crate::helpers::cache_dir();
|
|
|
let mut cache: PathBuf = crate::helpers::cache_dir();
|
|
|
cache.push("templates.json");
|
|
|
cache.push("templates.json");
|
|
|
debug!("Using cache path for templates: {}", cache.to_str().unwrap_or("err"));
|
|
|
debug!("Using cache path for templates: {}", cache.to_str().unwrap_or("err"));
|
|
@ -93,22 +125,22 @@ impl GithubTemplates { |
|
|
/// Reads the templates from the file cache
|
|
|
/// Reads the templates from the file cache
|
|
|
fn from_cache() -> Result<GithubTemplates> {
|
|
|
fn from_cache() -> Result<GithubTemplates> {
|
|
|
trace!("Reading templates from file cache");
|
|
|
trace!("Reading templates from file cache");
|
|
|
|
|
|
if !Self::is_cached() {
|
|
|
|
|
|
debug!("Templates response not yet cached");
|
|
|
|
|
|
return Err("Results are not yet cached".into());
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
let f = File::open(Self::cache_path())
|
|
|
let f = File::open(Self::cache_path())
|
|
|
.chain_err(|| "Error while opening cache file for templates")?;
|
|
|
.chain_err(|| "Error while opening cache file for templates")?;
|
|
|
let reader = BufReader::new(f);
|
|
|
let reader = BufReader::new(f);
|
|
|
let tpls = serde_json::from_reader(reader)
|
|
|
|
|
|
|
|
|
let tpls: Vec<Template> = serde_json::from_reader(reader)
|
|
|
.chain_err(|| "Error while reading templates from file cache")?;
|
|
|
.chain_err(|| "Error while reading templates from file cache")?;
|
|
|
|
|
|
debug!("Deserialized {} templates from file cache", tpls.len());
|
|
|
Ok(GithubTemplates { templates: tpls })
|
|
|
Ok(GithubTemplates { templates: tpls })
|
|
|
}
|
|
|
}
|
|
|
|
|
|
|
|
|
/// Creates a new struct with templates
|
|
|
/// Creates a new struct with templates
|
|
|
pub fn new() -> Result<GithubTemplates> {
|
|
|
|
|
|
if Self::is_cached() {
|
|
|
|
|
|
Self::from_cache()
|
|
|
|
|
|
} else {
|
|
|
|
|
|
Self::from_server()
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
pub fn new() -> Result<GithubTemplates> { Self::from_cache().or_else(|_| Self::from_server()) }
|
|
|
|
|
|
|
|
|
/// Returns a list of the template names
|
|
|
/// Returns a list of the template names
|
|
|
pub fn list_names(&self) -> Vec<&str> {
|
|
|
pub fn list_names(&self) -> Vec<&str> {
|
|
|