Skip to content

Commit 3b87e6a

Browse files
committed
Add exit codes to man page
1 parent 7e77ad0 commit 3b87e6a

4 files changed

Lines changed: 85 additions & 20 deletions

File tree

README.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -672,10 +672,13 @@ Options:
672672

673673
### Exit codes
674674

675-
- `0` for success (all links checked successfully or excluded/skipped as configured)
676-
- `1` for missing inputs and any unexpected runtime failures or config errors
677-
- `2` for link check failures (if any non-excluded link failed the check)
678-
- `3` for errors in the config file
675+
0 Success. The operation was completed successfully as instructed.
676+
677+
1 Missing inputs or any unexpected runtime failures or configuration errors
678+
679+
2 Link check failures. At least one non-excluded link failed the check.
680+
681+
3 Encountered errors in the config file.
679682

680683
### Ignoring links
681684

lychee-bin/src/commands/generate.rs

Lines changed: 64 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,25 @@
55
66
use anyhow::Result;
77
use clap::{CommandFactory, crate_authors};
8+
use clap_mangen::{
9+
Man,
10+
roff::{Roff, roman},
11+
};
812
use serde::Deserialize;
913
use strum::{Display, EnumIter, EnumString, VariantNames};
1014

1115
use crate::LycheeOptions;
1216

1317
const CONTRIBUTOR_THANK_NOTE: &str = "\n\nA huge thank you to all the wonderful contributors who helped make this project a success.";
18+
const EXIT_CODE_DESCRIPTION: &'static str = "
19+
0 Success. The operation was completed successfully as instructed.
20+
21+
1 Missing inputs or any unexpected runtime failures or configuration errors
22+
23+
2 Link check failures. At least one non-excluded link failed the check.
24+
25+
3 Encountered errors in the config file.
26+
";
1427

1528
/// What to generate when provided the --generate flag
1629
#[derive(Debug, Deserialize, Clone, Display, EnumIter, EnumString, VariantNames, PartialEq)]
@@ -33,22 +46,38 @@ fn man_page() -> Result<String> {
3346
let date = chrono::offset::Local::now().format("%Y-%m-%d");
3447
let authors = crate_authors!("\n\n").to_owned() + CONTRIBUTOR_THANK_NOTE;
3548

36-
let man =
37-
clap_mangen::Man::new(LycheeOptions::command().author(authors)).date(format!("{date}"));
38-
49+
let man = Man::new(LycheeOptions::command().author(authors)).date(format!("{date}"));
3950
let mut buffer: Vec<u8> = Vec::default();
40-
man.render(&mut buffer)?;
51+
52+
// Manually customise `Man::render` (see https://github.com/clap-rs/clap/issues/3354)
53+
man.render_title(&mut buffer)?;
54+
man.render_name_section(&mut buffer)?;
55+
man.render_synopsis_section(&mut buffer)?;
56+
man.render_description_section(&mut buffer)?;
57+
man.render_options_section(&mut buffer)?;
58+
render_exit_codes(&mut buffer)?;
59+
man.render_version_section(&mut buffer)?;
60+
man.render_authors_section(&mut buffer)?;
4161

4262
Ok(std::str::from_utf8(&buffer)?.to_owned())
4363
}
4464

65+
fn render_exit_codes(buffer: &mut Vec<u8>) -> Result<(), anyhow::Error> {
66+
let mut roff = Roff::default();
67+
roff.control("SH", ["EXIT CODES"]);
68+
roff.text([roman(EXIT_CODE_DESCRIPTION)]);
69+
roff.to_writer(buffer)?;
70+
Ok(())
71+
}
72+
4573
#[cfg(test)]
4674
mod tests {
4775
use super::man_page;
76+
use crate::generate::EXIT_CODE_DESCRIPTION;
4877
use anyhow::Result;
4978

5079
#[test]
51-
fn test_man_pages() -> Result<()> {
80+
fn test_man_page() -> Result<()> {
5281
let roff = man_page()?;
5382

5483
// Must contain description
@@ -67,4 +96,34 @@ mod tests {
6796
assert_eq!(roff.matches("\\-\\-version").count(), 2);
6897
Ok(())
6998
}
99+
100+
/// Test that the Exit Codes section in `README.md` is up to date with
101+
/// lychee's manual page.
102+
#[test]
103+
#[cfg(unix)]
104+
fn test_readme_exit_codes_up_to_date() -> Result<(), Box<dyn std::error::Error>> {
105+
use test_utils::load_readme_text;
106+
107+
const BEGIN: &str = "### Exit codes";
108+
const END: &str = "# ";
109+
110+
let readme = load_readme_text!();
111+
let start = readme.find(BEGIN).ok_or("Beginning not found in README")? + BEGIN.len();
112+
let end = readme[start..].find(END).ok_or("End not found in README")? - END.len();
113+
114+
let section = &readme[start..start + end];
115+
assert_eq!(
116+
filter_empty_lines(section),
117+
filter_empty_lines(EXIT_CODE_DESCRIPTION)
118+
);
119+
120+
Ok(())
121+
}
122+
123+
fn filter_empty_lines(s: &str) -> String {
124+
s.lines()
125+
.filter(|line| !line.trim().is_empty())
126+
.collect::<Vec<_>>()
127+
.join("\n")
128+
}
70129
}

lychee-bin/tests/usage.rs

Lines changed: 3 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
#[cfg(test)]
22
mod readme {
3-
use std::{fs, path::Path};
4-
53
use assert_cmd::Command;
64
use pretty_assertions::assert_eq;
75

@@ -12,14 +10,6 @@ mod readme {
1210
Command::cargo_bin(env!("CARGO_PKG_NAME")).expect("Couldn't get cargo package name")
1311
}
1412

15-
fn load_readme_text() -> String {
16-
let readme_path = Path::new(env!("CARGO_MANIFEST_DIR"))
17-
.parent()
18-
.unwrap()
19-
.join("README.md");
20-
fs::read_to_string(readme_path).unwrap()
21-
}
22-
2313
/// Remove line `[default: lychee/x.y.z]` from the string
2414
fn remove_lychee_version_line(string: &str) -> String {
2515
string
@@ -44,6 +34,8 @@ mod readme {
4434
#[test]
4535
#[cfg(unix)]
4636
fn test_readme_usage_up_to_date() -> Result<(), Box<dyn std::error::Error>> {
37+
use test_utils::load_readme_text;
38+
4739
let mut cmd = main_command();
4840

4941
let help_cmd = cmd.env_clear().arg("--help").assert().success();
@@ -54,7 +46,7 @@ mod readme {
5446
let usage_in_help = &help_output[usage_in_help_start..];
5547

5648
let usage_in_help = trim_empty_lines(&remove_lychee_version_line(usage_in_help));
57-
let readme = load_readme_text();
49+
let readme = load_readme_text!();
5850
let usage_start = readme
5951
.find(USAGE_STRING)
6052
.ok_or("Usage not found in README")?;

test-utils/src/lib.rs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,3 +149,14 @@ macro_rules! fixture_uri {
149149
.expect("expected subpath to form a valid URL")
150150
}};
151151
}
152+
153+
#[macro_export]
154+
macro_rules! load_readme_text {
155+
() => {{
156+
let readme_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
157+
.parent()
158+
.unwrap()
159+
.join("README.md");
160+
std::fs::read_to_string(readme_path).unwrap()
161+
}};
162+
}

0 commit comments

Comments
 (0)