|
|
@ -1,6 +1,17 @@ |
|
|
|
//! This module contains structs for working with the github templates
|
|
|
|
|
|
|
|
use log::{debug, trace};
|
|
|
|
use reqwest::blocking::Client;
|
|
|
|
use reqwest::header::{ACCEPT, CONTENT_TYPE, USER_AGENT};
|
|
|
|
use serde::{Deserialize, Serialize};
|
|
|
|
use std::fs::File;
|
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::io::BufReader;
|
|
|
|
use std::path::PathBuf;
|
|
|
|
|
|
|
|
use crate::errors::*;
|
|
|
|
|
|
|
|
pub const DEFAULT_USER_AGENT: &str = "gitig";
|
|
|
|
|
|
|
|
#[derive(Default, Debug, Clone, PartialEq, Serialize, Deserialize)]
|
|
|
|
#[serde(rename_all = "camelCase")]
|
|
|
@ -39,9 +50,72 @@ impl Template { |
|
|
|
///
|
|
|
|
/// if `!self.is_gitignore_template()` the whole `self.name` is returned
|
|
|
|
pub fn pretty_name(&self) -> &str {
|
|
|
|
if let Some(dot_index) = self.name.rfind(".") {
|
|
|
|
return &self.name[0..dot_index];
|
|
|
|
if let Some(dot_index) = self.name.rfind('.') {
|
|
|
|
return &self.name.get(0..dot_index).unwrap_or_else(|| self.name.as_str());
|
|
|
|
}
|
|
|
|
return self.name.as_str();
|
|
|
|
self.name.as_str()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// This struct holds the information about the templates available at github
|
|
|
|
pub struct GithubTemplates {
|
|
|
|
/// The templates
|
|
|
|
templates: Vec<Template>,
|
|
|
|
}
|
|
|
|
|
|
|
|
impl GithubTemplates {
|
|
|
|
/// Loads the templates from the github api
|
|
|
|
fn from_server() -> Result<GithubTemplates> {
|
|
|
|
trace!("Loading templates from github api");
|
|
|
|
let client = Client::new();
|
|
|
|
let res = client
|
|
|
|
.get("https://api.github.com/repos/github/gitignore/contents//")
|
|
|
|
.header(ACCEPT, "application/jsonapplication/vnd.github.v3+json")
|
|
|
|
.header(CONTENT_TYPE, "application/json")
|
|
|
|
.header(USER_AGENT, format!("{} {}", DEFAULT_USER_AGENT, env!("CARGO_PKG_VERSION")))
|
|
|
|
.send()
|
|
|
|
.chain_err(|| "Error while sending request to query all templates")?;
|
|
|
|
let body: Vec<Template> = res.json().chain_err(|| "json")?;
|
|
|
|
Ok(GithubTemplates { templates: body })
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns whether there is an cached json version
|
|
|
|
fn is_cached() -> bool { Self::cache_path().exists() }
|
|
|
|
|
|
|
|
/// Returns the path of the file cache
|
|
|
|
fn cache_path() -> PathBuf {
|
|
|
|
let mut cache: PathBuf = crate::helpers::cache_dir();
|
|
|
|
cache.push("templates.json");
|
|
|
|
debug!("Using cache path for templates: {}", cache.to_str().unwrap_or("err"));
|
|
|
|
cache
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Reads the templates from the file cache
|
|
|
|
fn from_cache() -> Result<GithubTemplates> {
|
|
|
|
trace!("Reading templates from file cache");
|
|
|
|
let f = File::open(Self::cache_path())
|
|
|
|
.chain_err(|| "Error while opening cache file for templates")?;
|
|
|
|
let reader = BufReader::new(f);
|
|
|
|
let tpls = serde_json::from_reader(reader)
|
|
|
|
.chain_err(|| "Error while reading templates from file cache")?;
|
|
|
|
Ok(GithubTemplates { templates: tpls })
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Creates a new struct with templates
|
|
|
|
pub fn new() -> Result<GithubTemplates> {
|
|
|
|
if Self::is_cached() {
|
|
|
|
Self::from_cache()
|
|
|
|
} else {
|
|
|
|
Self::from_server()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// Returns a list of the template names
|
|
|
|
pub fn list_names(&self) -> Vec<&str> {
|
|
|
|
self.templates
|
|
|
|
.iter()
|
|
|
|
.filter(|t| t.is_gitignore_template())
|
|
|
|
.map(Template::pretty_name)
|
|
|
|
.collect()
|
|
|
|
}
|
|
|
|
}
|