diff --git a/README.md b/README.md index cf0d560..7888717 100644 --- a/README.md +++ b/README.md @@ -45,6 +45,7 @@ fn main() { Section::new("usage note") .paragraph("This program will overwrite any file currently stored at the output path") ) + .exit_status(ExitStatus::default()) .render(); println!("{}", page); diff --git a/examples/main.rs b/examples/main.rs index 75f7439..6f327da 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -31,6 +31,7 @@ fn main() { .long("--port") .help("The network port to listen to."), ) + .exit_status(ExitStatus::default()) .example( Example::new() .text("listen on port 3000") @@ -43,11 +44,6 @@ fn main() { .prompt("#") .command("auth-service"), ) - .custom( - Section::new("custom section") - .paragraph("text for the custom section") - .paragraph("Additional text for the custom section"), - ) .author(Author::new("Alice Person").email("alice@person.com")) .author(Author::new("Bob Human").email("bob@human.com")) .render(); diff --git a/examples/without-defaults.rs b/examples/without-defaults.rs new file mode 100644 index 0000000..f604d7a --- /dev/null +++ b/examples/without-defaults.rs @@ -0,0 +1,61 @@ +extern crate man; + +use man::prelude::*; + +fn main() { + let msg = Manual::new("auth-service") + .arg(Arg::new("path")) + .env(Env::new("PORT").help("The network port to listen to")) + .flag( + Flag::new() + .short("-h") + .long("--help") + .help("Prints help information."), + ) + .flag( + Flag::new() + .short("-V") + .long("--version") + .help("Prints version information."), + ) + .flag( + Flag::new() + .short("-v") + .long("--verbosity") + .help("Pass multiple times to print more information."), + ) + .option( + Opt::new("port") + .short("-p") + .long("--port") + .help("The network port to listen to."), + ) + .custom( + Section::new("custom section") + .paragraph("text for the custom section") + .paragraph("Additional text for the custom section"), + ) + .custom( + Section::new("second custom section") + .paragraph("text for the custom section") + .paragraph("Additional text for the custom section"), + ) + .exit_status(ExitStatus::new(0)) + .example( + Example::new() + .text("listen on port 3000") + .command("auth-service -p 3000") + .output("now listening on port 3000"), + ) + .example( + Example::new() + .text("auth-service may need to be run by root") + .prompt("#") + .command("auth-service"), + ) + .author(Author::new("Alice Person").email("alice@person.com")) + .author(Author::new("Bob Human").email("bob@human.com")) + .render(); + + println!("{}", msg); +} diff --git a/src/exit_status.rs b/src/exit_status.rs new file mode 100644 index 0000000..db47937 --- /dev/null +++ b/src/exit_status.rs @@ -0,0 +1,20 @@ +/// Add a exit status section +#[derive(Debug, Clone, Default)] +pub struct ExitStatus { + pub(crate) code: Option, + pub(crate) description: Option<&'static str>, +} + +impl ExitStatus { + pub fn new(code: i32) -> Self { + Self { + code: Some(code), + description: None, + } + } + + pub fn description(mut self, description: &'static str) -> Self { + self.description = Some(description); + self + } +} diff --git a/src/lib.rs b/src/lib.rs index a85b0f4..59fa4ff 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -9,6 +9,7 @@ mod arg; mod author; mod environment; mod example; +mod exit_status; mod flag; mod man; mod option; @@ -20,6 +21,7 @@ pub use arg::Arg; pub use author::Author; pub use environment::Env; pub use example::Example; +pub use exit_status::ExitStatus; pub use flag::Flag; pub use man::Manual; pub use option::Opt; diff --git a/src/man.rs b/src/man.rs index 205ee39..8784d82 100644 --- a/src/man.rs +++ b/src/man.rs @@ -13,9 +13,50 @@ pub struct Manual { environment: Vec, arguments: Vec, custom_sections: Vec
, + exit_statuses: ExitStatuses, examples: Vec, } +#[derive(Debug, Clone)] +enum ExitStatuses { + DefaultStatuses, + CustomStatuses(Vec), +} + +static DEFAULT_STATUSES: [ExitStatus; 3] = [ + ExitStatus { + code: Some(0), + description: Some("Successful program execution."), + }, + ExitStatus { + code: Some(1), + description: Some("Unsuccessful program execution."), + }, + ExitStatus { + code: Some(101), + description: Some("The program panicked."), + }, +]; + +impl ExitStatuses { + fn push(&mut self, new_status: ExitStatus) { + if let ExitStatuses::CustomStatuses(vec) = self { + vec.push(new_status); + } + } + + fn set_to_default(&mut self) { + *self = ExitStatuses::DefaultStatuses; + } + + fn iter(&self) -> impl Iterator { + match self { + ExitStatuses::CustomStatuses(vec) => vec.iter(), + ExitStatuses::DefaultStatuses => DEFAULT_STATUSES.iter(), + } + } +} + impl Manual { /// Create a new instance. pub fn new(name: &str) -> Self { @@ -29,6 +70,7 @@ impl Manual { arguments: vec![], environment: vec![], custom_sections: vec![], + exit_statuses: ExitStatuses::CustomStatuses(vec![]), examples: vec![], } } @@ -89,6 +131,18 @@ impl Manual { self } + pub fn exit_status(mut self, exit_status: ExitStatus) -> Self { + match exit_status { + ExitStatus { code: None, .. } => { + self.exit_statuses.set_to_default(); + } + _ => { + self.exit_statuses.push(exit_status); + } + } + self + } + /// Render to a string. pub fn render(self) -> String { let man_num = 1; @@ -108,7 +162,7 @@ impl Manual { for section in self.custom_sections.into_iter() { page = custom(page, section); } - page = exit_status(page); + page = exit_status(page, &self.exit_statuses); page = examples(page, &self.examples); page = authors(page, &self.authors); page.render() @@ -336,9 +390,9 @@ fn env(page: Roff, environment: &[Env]) -> Roff { /// Create a `EXIT STATUS` section. /// -/// ## Implementation Note -/// This currently only returns the status code `0`, and takes no arguments. We -/// should let it take arguments. +/// If initialized with the default method, this will have status codes of 0 +/// (success), 1 (failure), and 101 (panic). Alternatively, it can be initialized +/// with custom codes and descriptions. /// /// ## Formatting /// ```txt @@ -349,15 +403,18 @@ fn env(page: Roff, environment: &[Env]) -> Roff { /// /// 2 Optional error /// ``` -fn exit_status(page: Roff) -> Roff { - page.section( - "EXIT STATUS", - &[ - list(&[bold("0")], &["Successful program execution.\n\n"]), - list(&[bold("1")], &["Unsuccessful program execution.\n\n"]), - list(&[bold("101")], &["The program panicked."]), - ], - ) +fn exit_status(page: Roff, exit_statuses: &ExitStatuses) -> Roff { + if exit_statuses.iter().next().is_none() { + return page; + } + let mut arr = vec![]; + for status in exit_statuses.iter() { + let code = format!("{}", status.code.expect("set by `.new()` method")); + let mut description = String::from(status.description.unwrap_or("")); + description.push_str("\n\n"); + arr.push(list(&[bold(&code)], &[description])); + } + page.section("exit status", &arr) } /// Create a custom section. diff --git a/src/prelude.rs b/src/prelude.rs index d25f8db..cdf4f58 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -13,6 +13,7 @@ pub use arg::Arg; pub use author::Author; pub use environment::Env; pub use example::Example; +pub use exit_status::ExitStatus; pub use flag::Flag; pub use man::Manual; pub use option::Opt;