|
1 | 1 | mod cli; |
2 | 2 |
|
3 | | -use clap::Parser; |
| 3 | +use clap::{CommandFactory, Parser}; |
4 | 4 | use cli::{Cli, Command}; |
5 | 5 | use git_ledger::{IdStrategy, Ledger, Mutation}; |
6 | 6 | use git2::Repository; |
7 | 7 | use std::io::Read; |
8 | | -use std::path::Path; |
| 8 | +use std::path::{Path, PathBuf}; |
9 | 9 | use std::process; |
10 | 10 |
|
11 | 11 | fn main() { |
| 12 | + if let Some(dir) = parse_generate_man_flag() { |
| 13 | + if let Err(e) = generate_man_page(dir) { |
| 14 | + eprintln!("Error: {}", e); |
| 15 | + process::exit(1); |
| 16 | + } |
| 17 | + return; |
| 18 | + } |
| 19 | + |
12 | 20 | let cli = Cli::parse(); |
13 | 21 |
|
14 | 22 | if let Err(e) = run(&cli) { |
@@ -148,3 +156,66 @@ fn run(cli: &Cli) -> Result<(), Box<dyn std::error::Error>> { |
148 | 156 |
|
149 | 157 | Ok(()) |
150 | 158 | } |
| 159 | + |
| 160 | +/// Check for `--generate-man <DIR>` before clap parses, so it doesn't |
| 161 | +/// conflict with the required subcommand. |
| 162 | +fn parse_generate_man_flag() -> Option<PathBuf> { |
| 163 | + let args: Vec<String> = std::env::args().collect(); |
| 164 | + let pos = args.iter().position(|a| a == "--generate-man")?; |
| 165 | + let dir = args |
| 166 | + .get(pos + 1) |
| 167 | + .map(PathBuf::from) |
| 168 | + .unwrap_or_else(default_man_dir); |
| 169 | + Some(dir) |
| 170 | +} |
| 171 | + |
| 172 | +fn default_man_dir() -> PathBuf { |
| 173 | + std::env::var_os("XDG_DATA_HOME") |
| 174 | + .map(PathBuf::from) |
| 175 | + .unwrap_or_else(|| { |
| 176 | + let home = std::env::var_os("HOME").expect("HOME is not set"); |
| 177 | + PathBuf::from(home).join(".local/share") |
| 178 | + }) |
| 179 | + .join("man") |
| 180 | +} |
| 181 | + |
| 182 | +fn generate_man_page(output_dir: PathBuf) -> Result<(), Box<dyn std::error::Error>> { |
| 183 | + let man1_dir = output_dir.join("man1"); |
| 184 | + std::fs::create_dir_all(&man1_dir)?; |
| 185 | + |
| 186 | + let cmd = Cli::command(); |
| 187 | + let man = clap_mangen::Man::new(cmd); |
| 188 | + let mut buffer = Vec::new(); |
| 189 | + man.render(&mut buffer)?; |
| 190 | + |
| 191 | + let man_path = man1_dir.join("git-ledger.1"); |
| 192 | + std::fs::write(&man_path, buffer)?; |
| 193 | + |
| 194 | + let output_dir = output_dir.canonicalize()?; |
| 195 | + eprintln!("Wrote man page to {}", man_path.canonicalize()?.display()); |
| 196 | + |
| 197 | + if !manpath_covers(&output_dir) { |
| 198 | + eprintln!(); |
| 199 | + eprintln!("You may need to add this to your shell environment:"); |
| 200 | + eprintln!(); |
| 201 | + eprintln!(" export MANPATH=\"{}:$MANPATH\"", output_dir.display()); |
| 202 | + } |
| 203 | + Ok(()) |
| 204 | +} |
| 205 | + |
| 206 | +/// Returns `true` if `dir` is equal to, or a subdirectory of, any component |
| 207 | +/// in the `MANPATH` environment variable. |
| 208 | +fn manpath_covers(dir: &std::path::Path) -> bool { |
| 209 | + let Some(manpath) = std::env::var_os("MANPATH") else { |
| 210 | + return false; |
| 211 | + }; |
| 212 | + for component in std::env::split_paths(&manpath) { |
| 213 | + let Ok(component) = component.canonicalize() else { |
| 214 | + continue; |
| 215 | + }; |
| 216 | + if dir.starts_with(&component) { |
| 217 | + return true; |
| 218 | + } |
| 219 | + } |
| 220 | + false |
| 221 | +} |
0 commit comments