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.

125 lines
4.1 KiB

4 years ago
4 years ago
4 years ago
3 years ago
3 years ago
  1. /*! Functions and templates which can be imported by app.rs to save effort */
  2. // Copyright 2017-2019, Stephan Sokolow
  3. // FIXME: Report that StructOpt is tripping Clippy's `result_unwrap_used` lint (which I use to push
  4. // for .expect() instead) in my two Option<T> fields and the `allow` gets ignored unless I
  5. // `#![...]` it onto the entire module.
  6. #![allow(clippy::result_unwrap_used)]
  7. use log::{info, trace};
  8. use crate::cache::File as FileCache;
  9. use crate::errors::*;
  10. use crate::{helpers, template::GithubTemplates};
  11. use directories::ProjectDirs;
  12. use std::{env, path::PathBuf};
  13. use structopt::StructOpt;
  14. /// Modified version of Clap's default template for proper help2man compatibility
  15. ///
  16. /// Used as a workaround for:
  17. /// 1. Clap's default template interfering with `help2man`'s proper function
  18. /// ([clap-rs/clap/#1432](https://github.com/clap-rs/clap/issues/1432))
  19. /// 2. Workarounds involving injecting `\n` into the description breaking help output if used
  20. /// on subcommand descriptions.
  21. pub const HELP_TEMPLATE: &str = "{bin} {version}
  22. {about}
  23. USAGE:
  24. {usage}
  25. {all-args}
  26. ";
  27. /// Options used by boilerplate code
  28. #[derive(StructOpt, Debug)]
  29. #[structopt(rename_all = "kebab-case")]
  30. pub struct BoilerplateOpts {
  31. /// Decrease verbosity (-q, -qq, -qqq, etc.)
  32. #[structopt(short, long, parse(from_occurrences))]
  33. pub quiet: u64,
  34. /// Increase verbosity (-v, -vv, -vvv, etc.)
  35. #[structopt(short, long, parse(from_occurrences))]
  36. pub verbose: u64,
  37. /// Display timestamps on log messages (sec, ms, ns, none)
  38. #[structopt(short, long, value_name = "resolution")]
  39. pub timestamp: Option<stderrlog::Timestamp>,
  40. }
  41. /// Checks if the given dir contains the root `.git` dir or file
  42. ///
  43. /// Submodules do not contain a complete .git directory, but only a .git file with a reference to
  44. /// the actual folder. This function takes both into account.
  45. ///
  46. /// # Errors
  47. ///
  48. /// Returns an `Err` if any of the fs related methods return an error
  49. fn has_git_dir(path: &PathBuf) -> Result<bool> {
  50. trace!("Checking for git root in {:?}", &path);
  51. let mut git: PathBuf = path.clone();
  52. git.push(".git");
  53. for entry in path.read_dir().chain_err(|| "Reading contents of dir")? {
  54. if let Ok(entry) = entry {
  55. // check if this is `.git`
  56. if entry.path() == git {
  57. return Ok(true);
  58. }
  59. }
  60. }
  61. Ok(false)
  62. }
  63. /// Returns the root git directory for the current directory if there is one
  64. ///
  65. /// # Errors
  66. ///
  67. /// Returns an [`Err`](std::Err) if the current cwd is invalid (refer to
  68. /// [`current_dir`](std::env::current_dir))
  69. pub fn git_dir() -> Result<Option<PathBuf>> {
  70. let mut cwd: Option<PathBuf> =
  71. Some(std::env::current_dir().chain_err(|| "Error with current dir")?);
  72. while cwd.is_some() {
  73. let c = cwd.ok_or("Should not have been none, as checked before in if")?;
  74. if has_git_dir(&c)? {
  75. return Ok(Some(c));
  76. }
  77. cwd = c.parent().map(PathBuf::from);
  78. }
  79. info!("Arrived at filesystem root while checking for git folder");
  80. Ok(None)
  81. }
  82. /// Returns a `PathBuf` to the cache root for this project
  83. ///
  84. /// This will be either the appropriate cache dir according to XDG or as a fallback `/tmp`.
  85. pub fn cache_root() -> PathBuf {
  86. match ProjectDirs::from("org", "webschneider", env!("CARGO_PKG_NAME")) {
  87. Some(dirs) => PathBuf::from(dirs.cache_dir()),
  88. None => "/tmp".into(),
  89. }
  90. }
  91. /// Returns a default file cache
  92. pub fn default_cache() -> Result<FileCache> {
  93. let cache_root = crate::helpers::cache_root();
  94. FileCache::new(&cache_root, std::time::Duration::from_secs(60 * 24 * 2))
  95. }
  96. /// Returns a `GithubTemplates` struct with all available templates
  97. pub fn get_templates() -> Result<GithubTemplates> {
  98. let cache = helpers::default_cache().chain_err(|| "Error while creating cache")?;
  99. let tmpl = if cache.exists("templates.json") {
  100. cache.get("templates.json")?
  101. } else {
  102. let tmpls = GithubTemplates::new().chain_err(|| "Error while getting Templates")?;
  103. cache
  104. .set("templates.json", &tmpls)
  105. .chain_err(|| "Error while writing templates to cache")?;
  106. tmpls
  107. };
  108. Ok(tmpl)
  109. }