A cli program to easily handle .gitignore files
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 

141 lines
4.9 KiB

/*! Application-specific logic lives here */
// Parts Copyright 2017-2019, Stephan Sokolow
// Standard library imports
use std::path::PathBuf;
// 3rd-party crate imports
use reqwest::blocking::Client;
use reqwest::header::{ACCEPT, CONTENT_TYPE, USER_AGENT};
use structopt::StructOpt;
use log::{debug, error, info, trace, warn};
// Local Imports
use crate::errors::*;
use crate::gitignore::Gitignore;
use crate::helpers::{git_dir, BoilerplateOpts, HELP_TEMPLATE};
use crate::template::*;
/// The verbosity level when no `-q` or `-v` arguments are given, with `0` being `-q`
pub const DEFAULT_VERBOSITY: u64 = 1;
pub const DEFAULT_USER_AGENT: &str = "gitig";
/// Command-line argument schema
///
/// ## Relevant Conventions:
///
/// * Make sure that there is a blank space between the `<name>` `<version>` line and the
/// description text or the `--help` output won't comply with the platform conventions that
/// `help2man` depends on to generate your manpage. (Specifically, it will mistake the `<name>
/// <version>` line for part of the description.)
/// * `StructOpt`'s default behaviour of including the author name in the `--help` output is an
/// oddity among Linux commands and, if you don't disable it, you run the risk of people
/// unfamiliar with `StructOpt` assuming that you are an egotistical person who made a conscious
/// choice to add it.
///
/// The proper standardized location for author information is the `AUTHOR` section which you
/// can read about by typing `man help2man`.
///
/// ## Cautions:
/// * Subcommands do not inherit `template` and it must be re-specified for each one. ([clap-rs/clap#1184](https://github.com/clap-rs/clap/issues/1184))
/// * Double-check that your choice of `about` or `long_about` is actually overriding this
/// doc comment. The precedence is affected by things you wouldn't expect, such as the presence
/// or absence of `template` and it's easy to wind up with this doc-comment as your `--help`
/// ([TeXitoi/structopt#173](https://github.com/TeXitoi/structopt/issues/173))
/// * Do not begin the description text for subcommands with `\n`. It will break the formatting in
/// the top-level help output's list of subcommands.
#[derive(StructOpt, Debug)]
#[structopt(template = HELP_TEMPLATE,
about = "TODO: Replace me with the description text for the command",
global_setting = structopt::clap::AppSettings::ColoredHelp)]
pub struct CliOpts {
#[allow(clippy::missing_docs_in_private_items)] // StructOpt won't let us document this
#[structopt(flatten)]
pub boilerplate: BoilerplateOpts,
/// Subcommands
#[structopt(subcommand)]
cmd: Command,
}
/// Subcommands
#[derive(StructOpt, Debug)]
pub enum Command {
/// Add a line to the gitignore
Add {
/// The glob string that should be added
glob: String,
},
/// Download a gitignore for a language
Get {
/// The language for which the gitignore should be downloaded
lang: String,
},
/// List all available templates that can be downloaded
ListTemplates,
}
/// Runs the command `add`
fn run_add(glob: &str) -> Result<()> {
trace!("running command `add` with glob '{}'", &glob);
let root = match git_dir()? {
Some(r) => r,
None => return Err(ErrorKind::NoGitRootFound.into()),
};
info!("Working with git root in {:?}", root);
let mut file_path = PathBuf::from(&root);
file_path.push(".gitignore");
let gitig = Gitignore::from_path(&file_path);
gitig.add_line(glob)?;
Ok(())
}
/// Runs the command `get`
fn run_get(lang: &str) -> Result<()> {
unimplemented!();
}
/// Runs the command `list-templates`
fn run_list_templates() -> Result<()> {
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")?;
body.iter().filter(|t| t.is_gitignore_template()).for_each(|t| println!("{}", t.pretty_name()));
Ok(())
}
/// The actual `main()`
pub fn main(opts: CliOpts) -> Result<()> {
match opts.cmd {
Command::Add { glob } => run_add(&glob)?,
Command::Get { lang } => run_get(&lang)?,
Command::ListTemplates => run_list_templates()?,
};
Ok(())
}
// Tests go below the code where they'll be out of the way when not the target of attention
#[cfg(test)]
mod tests {
use super::CliOpts;
// TODO: Unit test to verify that the doc comment on `CliOpts` isn't overriding the intended
// about string.
#[test]
/// Test something
fn test_something() {
// TODO: Test something
}
}