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.
117 lines
3.7 KiB
117 lines
3.7 KiB
/*! Functions and templates which can be imported by app.rs to save effort */
|
|
// Copyright 2017-2019, Stephan Sokolow
|
|
|
|
// FIXME: Report that StructOpt is tripping Clippy's `result_unwrap_used` lint (which I use to push
|
|
// for .expect() instead) in my two Option<T> fields and the `allow` gets ignored unless I
|
|
// `#![...]` it onto the entire module.
|
|
#![allow(clippy::result_unwrap_used)]
|
|
use log::{info, trace};
|
|
use std::fs::File;
|
|
|
|
use crate::errors::*;
|
|
use std::fs::DirEntry;
|
|
use std::fs::ReadDir;
|
|
use std::path::Path;
|
|
use std::path::PathBuf;
|
|
use structopt::{clap, StructOpt};
|
|
|
|
/// Modified version of Clap's default template for proper help2man compatibility
|
|
///
|
|
/// Used as a workaround for:
|
|
/// 1. Clap's default template interfering with `help2man`'s proper function
|
|
/// ([clap-rs/clap/#1432](https://github.com/clap-rs/clap/issues/1432))
|
|
/// 2. Workarounds involving injecting `\n` into the description breaking help output if used
|
|
/// on subcommand descriptions.
|
|
pub const HELP_TEMPLATE: &str = "{bin} {version}
|
|
|
|
{about}
|
|
|
|
USAGE:
|
|
{usage}
|
|
|
|
{all-args}
|
|
";
|
|
|
|
/// Options used by boilerplate code
|
|
#[derive(StructOpt, Debug)]
|
|
#[structopt(rename_all = "kebab-case")]
|
|
pub struct BoilerplateOpts {
|
|
/// Decrease verbosity (-q, -qq, -qqq, etc.)
|
|
#[structopt(short, long, parse(from_occurrences))]
|
|
pub quiet: u64,
|
|
|
|
/// Increase verbosity (-v, -vv, -vvv, etc.)
|
|
#[structopt(short, long, parse(from_occurrences))]
|
|
pub verbose: u64,
|
|
|
|
/// Display timestamps on log messages (sec, ms, ns, none)
|
|
#[structopt(short, long, value_name = "resolution")]
|
|
pub timestamp: Option<stderrlog::Timestamp>,
|
|
|
|
/// Write a completion definition for the specified shell to stdout (bash, zsh, etc.)
|
|
#[structopt(long, value_name = "shell")]
|
|
pub dump_completions: Option<clap::Shell>,
|
|
}
|
|
|
|
/// Checks if the given dir contains the root `.git` dir
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns an `Err` if any of the fs related methods return an error
|
|
fn has_git_dir(path: &PathBuf) -> Result<bool> {
|
|
trace!("Checking for git root in {:?}", &path);
|
|
let mut git: PathBuf = path.clone();
|
|
git.push(".git");
|
|
for entry in path.read_dir().chain_err(|| "Reading contents of dir")? {
|
|
if let Ok(entry) = entry {
|
|
// skip non-directories
|
|
if let Ok(ft) = entry.file_type() {
|
|
if !ft.is_dir() {
|
|
continue;
|
|
}
|
|
}
|
|
|
|
// check if this is `.git`
|
|
if entry.path() == git {
|
|
return Ok(true);
|
|
}
|
|
}
|
|
}
|
|
Ok(false)
|
|
}
|
|
|
|
/// Returns the root git directory for the current directory if there is one
|
|
///
|
|
/// # Errors
|
|
///
|
|
/// Returns an [`Err`](std::Err) if the current cwd is invalid (refer to
|
|
/// [`current_dir`](std::env::current_dir))
|
|
pub fn git_dir() -> Result<Option<PathBuf>> {
|
|
let mut cwd: Option<PathBuf> =
|
|
Some(std::env::current_dir().chain_err(|| "Error with current dir")?);
|
|
while cwd.is_some() {
|
|
let c = cwd.ok_or("Should not have been none, as checked before in if")?;
|
|
if has_git_dir(&c)? {
|
|
return Ok(Some(c));
|
|
}
|
|
cwd = c.parent().map(PathBuf::from);
|
|
}
|
|
|
|
info!("Arrived at filesystem root while checking for git folder");
|
|
Ok(None)
|
|
}
|
|
|
|
/// Returns the path to the cache dir
|
|
///
|
|
/// If env `XDG_CACHE_HOME` is not set it uses `$HOME/.cache` and appends `gitig`. If dir does not
|
|
/// exists it will be created
|
|
pub fn cache_dir() -> PathBuf {
|
|
let root = std::env::var("XDG_CACHE_HOME").unwrap_or_else(|_| "$HOME/.cache/".to_string());
|
|
let mut root = PathBuf::from(root);
|
|
root.push("gitig");
|
|
if !root.exists() && std::fs::create_dir_all(&root).is_err() {
|
|
// should not happen, but as a fallback we can use /tmp/
|
|
root = PathBuf::from("/tmp");
|
|
}
|
|
root
|
|
}
|