From d51a4c911ad689f68cf9d5b29ac3db8bfe164319 Mon Sep 17 00:00:00 2001 From: Marcel Schneider Date: Wed, 11 Mar 2020 14:16:21 +0100 Subject: [PATCH] implement `get` command --- src/app.rs | 17 ++++++-------- src/errors.rs | 7 ++++++ src/template.rs | 62 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 74 insertions(+), 12 deletions(-) diff --git a/src/app.rs b/src/app.rs index f2887ea..c0dd23e 100644 --- a/src/app.rs +++ b/src/app.rs @@ -96,24 +96,21 @@ fn run_add(glob: &str) -> Result<()> { /// Runs the command `get` fn run_get(lang: &str) -> Result<()> { trace!("Run command `get` with lang {}", &lang); - let root = match git_dir()? { + let mut root = match git_dir()? { Some(r) => r, None => return Err(ErrorKind::NoGitRootFound.into()), }; info!("Working with git root in {:?}", root); - let tmpls = helpers::get_templates()?; - let tmpl: &Template = + let mut tmpls = helpers::get_templates()?; + let tmpl: &mut Template = tmpls.get(lang).ok_or_else(|| ErrorKind::TemplateNotFound(lang.to_string()))?; debug!("Found a template for {}", lang); - let url = tmpl - .download_url - .as_ref() - .ok_or_else(|| ErrorKind::TemplateNoDownloadUrl(lang.to_string()))?; - trace!("Getting template for {} from {}", tmpl.pretty_name(), url); - - todo!(); + tmpl.load_content()?; + root.push(".gitignore"); + tmpl.write_to(&root)?; + trace!("Wrote template to file"); Ok(()) } diff --git a/src/errors.rs b/src/errors.rs index 5bc2a6a..329d1b0 100644 --- a/src/errors.rs +++ b/src/errors.rs @@ -3,6 +3,9 @@ // Create the Error, ErrorKind, ResultExt, and Result types error_chain! { + foreign_links{ + Io(::std::io::Error) #[doc = "Link to a `std::io::Error` type."]; + } errors{ NoGitRootFound { description("no git root found"), @@ -15,5 +18,9 @@ error_chain! { description("template has no download url"), display("template {} has no download url", name) } + TemplateNoContent { + description("template contains no content that could be used or written. try to `load_content` first.") + } } + } diff --git a/src/template.rs b/src/template.rs index 0f88d01..34405a6 100644 --- a/src/template.rs +++ b/src/template.rs @@ -4,6 +4,10 @@ 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::{BufWriter, Write}; + +use std::path::PathBuf; use crate::errors::*; @@ -30,6 +34,7 @@ pub struct Template { pub type_field: String, #[serde(rename = "_links")] pub links: Links, + pub content: Option, } /// Part of github api response @@ -56,6 +61,59 @@ impl Template { } self.name.as_str() } + + /// Loads the template content from github + pub fn load_content(&mut self) -> Result<()> { + let url = self + .download_url + .as_ref() + .ok_or_else(|| ErrorKind::TemplateNoDownloadUrl(self.pretty_name().into()))?; + debug!("Loading template content for {} from {}", self.pretty_name(), url); + let client = Client::new(); + let res = client + .get(url) + .header(ACCEPT, "text/html") + .header(USER_AGENT, format!("{} {}", DEFAULT_USER_AGENT, env!("CARGO_PKG_VERSION"))) + .send() + .chain_err(|| { + format!("Error while getting content of template {}", self.pretty_name()) + })?; + + debug!( + "Got a response from the server ({} B)", + res.content_length().map_or_else(|| "?".to_string(), |v| v.to_string()) + ); + + let body: String = + res.text().chain_err(|| "Error while parsing body from template response")?; + self.content = Some(body); + debug!("Set content for template {}", self.pretty_name()); + + Ok(()) + } + + /// Writes the content of this template to the given path + /// + /// Creates the file if it does not exist or trunceates an already existing one. + /// + /// # Errors + /// + /// Returns `ErrorKind::TemplateNoContent` if this templates has no content (i.e. + /// `load_content` has not yet been called. + /// Other reasons for `Err` can be io related errors. + #[allow(clippy::option_expect_used)] + pub fn write_to(&self, path: &PathBuf) -> Result<()> { + debug!("Writing contents of {} to {}", self.pretty_name(), path.to_string_lossy()); + if self.content.is_none() { + return Err(ErrorKind::TemplateNoContent.into()); + } + let file = File::create(path)?; + let mut writer = BufWriter::new(file); + + writer.write_all(self.content.as_ref().expect("checked before to be some").as_bytes())?; + trace!("Wrote all content"); + Ok(()) + } } /// This struct holds the information about the templates available at github @@ -97,9 +155,9 @@ impl GithubTemplates { } /// Returns the template for the given name, if found - pub fn get(&self, name: &str) -> Option<&Template> { + pub fn get(&mut self, name: &str) -> Option<&mut Template> { // names have all a .gitignore suffix let name = format!("{}.gitignore", name); - self.templates.iter().find(|t| t.name.eq_ignore_ascii_case(&name)) + self.templates.iter_mut().find(|t| t.name.eq_ignore_ascii_case(&name)) } }