From 0c639dba469c16c60221270480a2d2d4a417774f Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Mon, 21 Jan 2019 13:11:56 -0500 Subject: [PATCH 1/7] Revise exit status section This commit revises the exit section to allow the user to specify custom status codes and messages to correspond to those codes. --- README.md | 1 + examples/main.rs | 1 + src/exit_status.rs | 30 ++++ src/lib.rs | 2 + src/man.rs | 44 ++++-- src/mod.rs | 369 --------------------------------------------- src/prelude.rs | 1 + 7 files changed, 69 insertions(+), 379 deletions(-) create mode 100644 src/exit_status.rs diff --git a/README.md b/README.md index 08111c1..9c2f8b7 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,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 0603e1d..bd1021a 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -36,6 +36,7 @@ fn main() { .paragraph("text for the custom section") .paragraph("Additional text for the custom section"), ) + .exit_status(ExitStatus::default()) .author(Author::new("Alice Person").email("alice@person.com")) .author(Author::new("Bob Human").email("bob@human.com")) .render(); diff --git a/src/exit_status.rs b/src/exit_status.rs new file mode 100644 index 0000000..de78e45 --- /dev/null +++ b/src/exit_status.rs @@ -0,0 +1,30 @@ +/// Add a exit status section +#[derive(Debug, Clone, Default)] +pub struct ExitStatus { + pub(crate) code: Option, + pub(crate) description: Option<&'static str>, + pub(crate) use_default_instead: bool, +} + +impl ExitStatus { + pub fn new(code: i32) -> Self { + Self { + code: Some(code), + description: None, + use_default_instead: false, + } + } + + pub fn default() -> Self { + Self { + code: None, + description: None, + use_default_instead: true, + } + } + + 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 1ab846a..b06a930 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -8,6 +8,7 @@ extern crate roff; mod arg; mod author; mod environment; +mod exit_status; mod flag; mod man; mod option; @@ -18,6 +19,7 @@ pub mod prelude; pub use arg::Arg; pub use author::Author; pub use environment::Env; +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 4bce1da..6481743 100644 --- a/src/man.rs +++ b/src/man.rs @@ -13,6 +13,7 @@ pub struct Manual { environment: Vec, arguments: Vec, custom_sections: Vec
, + exit_statuses: Vec, } impl Manual { @@ -28,6 +29,7 @@ impl Manual { arguments: vec![], environment: vec![], custom_sections: vec![], + exit_statuses: vec![], } } @@ -81,6 +83,11 @@ impl Manual { self } + pub fn exit_status(mut self, exit_status: ExitStatus) -> Self { + self.exit_statuses.push(exit_status); + self + } + /// Render to a string. pub fn render(self) -> String { let man_num = 1; @@ -100,7 +107,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 = authors(page, &self.authors); page.render() } @@ -327,9 +334,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 @@ -340,15 +347,32 @@ fn env(page: Roff, environment: &[Env]) -> Roff { /// /// 2 Optional error /// ``` -fn exit_status(page: Roff) -> Roff { - page.section( - "EXIT STATUS", - &[ +fn exit_status(page: Roff, exit_statuses: &[ExitStatus]) -> Roff { + if exit_statuses.is_empty() { + return page; + } + let arr = if exit_statuses + .iter() + .any(|status| status.use_default_instead) + { + vec![ list(&[bold("0")], &["Successful program execution.\n\n"]), list(&[bold("1")], &["Unsuccessful program execution.\n\n"]), list(&[bold("101")], &["The program panicked."]), - ], - ) + ] + } else { + let mut arr = vec![]; + for exit_status in exit_statuses { + let exit_code = + format!("{}", exit_status.code.expect("initialized with value")); + let mut exit_description = + String::from(exit_status.description.unwrap_or("")); + exit_description.push_str("\n\n"); + arr.push(list(&[bold(&exit_code)], &[exit_description])); + } + arr + }; + page.section("EXIT STATUS", &arr) } /// Create a custom section. diff --git a/src/mod.rs b/src/mod.rs index dfcc070..e69de29 100644 --- a/src/mod.rs +++ b/src/mod.rs @@ -1,369 +0,0 @@ -mod author; -mod custom; -mod environment; -mod flag; -mod option; - -use self::author::Author; -use self::custom::Para; -use self::environment::Env; -use self::flag::Flag; -use self::option::Opt; -use roff::{bold, italic, list, Roff, Troffable}; -use std::convert::AsRef; - -/// Man page struct. -#[derive(Debug, Clone)] -pub struct Man { - name: String, - help: Option, - authors: Vec, - flags: Vec, - options: Vec, - environment: Vec, - arguments: Vec, - custom: Vec, -} - -impl Man { - /// Create a new instance. - pub fn new(name: &str) -> Self { - Self { - name: name.into(), - help: None, - authors: vec![], - flags: vec![], - options: vec![], - arguments: vec![], - environment: vec![], - } - } - - /// Add a help. - pub fn help(mut self, desc: &str) -> Self { - let desc = desc.into(); - self.help = Some(desc); - self - } - - /// Add an author. - pub fn author( - mut self, - name: impl AsRef, - email: Option, - ) -> Self { - self.authors.push(Author { - name: name.as_ref().to_owned(), - email, - }); - self - } - - /// Add an environment variable. - pub fn environment( - mut self, - name: String, - default: Option, - help: Option, - ) -> Self { - self.environment.push(Env { - name, - default, - help, - }); - self - } - - /// Add an flag. - pub fn flag( - mut self, - short: Option, - long: Option, - help: Option, - ) -> Self { - self.flags.push(Flag { short, long, help }); - self - } - - /// Add an option. - pub fn option( - mut self, - short: Option, - long: Option, - help: Option, - argument: String, - default: Option, - ) -> Self { - self.options.push(Opt { - short, - long, - help, - argument, - default, - }); - self - } - - /// Add a positional argument. The items are displayed in the order they're - /// pushed. - // TODO: make this accept argument vecs / optional args too. `arg...`, `arg?` - pub fn argument(mut self, arg: String) -> Self { - self.arguments.push(arg); - self - } - - pub fn render(self) -> String { - let man_num = 1; - let mut page = Roff::new(&self.name, man_num); - page = help(page, &self.name, &self.help); - page = synopsis( - page, - &self.name, - &self.flags, - &self.options, - &self.arguments, - ); - page = flags(page, &self.flags); - page = options(page, &self.options); - page = environment(page, &self.environment); - page = exit_status(page); - page = authors(page, &self.authors); - page.render() - } -} - -/// Create a `NAME` section. -/// -/// ## Formatting -/// ```txt -/// NAME -/// mycmd - brief help of the application -/// ``` -fn help(page: Roff, name: &str, desc: &Option) -> Roff { - let desc = match desc { - Some(ref desc) => format!("{} - {}", name, desc), - None => name.to_owned(), - }; - - page.section("NAME", &[desc]) -} - -/// Create a `SYNOPSIS` section. -fn synopsis( - page: Roff, - name: &str, - flags: &[Flag], - options: &[Opt], - args: &[String], -) -> Roff { - let flags = match flags.len() { - 0 => "".into(), - _ => " [FLAGS]".into(), - }; - let options = match options.len() { - 0 => "".into(), - _ => " [OPTIONS]".into(), - }; - - let mut msg = vec![]; - msg.push(bold(name)); - msg.push(flags); - msg.push(options); - - for arg in args { - msg.push(format!(" {}", arg)); - } - - page.section("SYNOPSIS", &msg) -} - -/// Create a `AUTHOR` or `AUTHORS` section. -/// -/// ## Formatting -/// ```txt -/// AUTHORS -/// Alice Person -/// Bob Human -/// ``` -fn authors(page: Roff, authors: &[Author]) -> Roff { - let title = match authors.len() { - 0 => return page, - 1 => "AUTHOR", - _ => "AUTHORS", - }; - - let last = authors.len() - 1; - let mut auth_values = vec![]; - auth_values.push(init_list()); - for (index, author) in authors.iter().enumerate() { - auth_values.push(author.name.to_owned()); - - if let Some(ref email) = author.email { - auth_values.push(format!(" <{}>", email)) - }; - - if index != last { - auth_values.push(format!("\n")); - } - } - - page.section(title, &auth_values) -} - -/// Create a `FLAGS` section. -/// -/// ## Formatting -/// ```txt -/// FLAGS -/// ``` -fn flags(page: Roff, flags: &[Flag]) -> Roff { - if flags.is_empty() { - return page; - } - - let last = flags.len() - 1; - let mut arr: Vec = vec![]; - for (index, flag) in flags.iter().enumerate() { - let mut args: Vec = vec![]; - if let Some(ref short) = flag.short { - args.push(bold(&short)); - } - if let Some(ref long) = flag.long { - if !args.is_empty() { - args.push(", ".to_string()); - } - args.push(bold(&long)); - } - let desc = match flag.help { - Some(ref desc) => desc.to_string(), - None => "".to_string(), - }; - arr.push(list(&args, &[desc])); - - if index != last { - arr.push(format!("\n\n")); - } - } - page.section("FLAGS", &arr) -} - -/// Create a `OPTIONS` section. -/// -/// ## Formatting -/// ```txt -/// OPTIONS -/// ``` -fn options(page: Roff, options: &[Opt]) -> Roff { - if options.is_empty() { - return page; - } - - let last = options.len() - 1; - let mut arr: Vec = vec![]; - for (index, opt) in options.iter().enumerate() { - let mut args: Vec = vec![]; - if let Some(ref short) = opt.short { - args.push(bold(&short)); - } - if let Some(ref long) = opt.long { - if !args.is_empty() { - args.push(", ".to_string()); - } - args.push(bold(&long)); - } - args.push("=".into()); - args.push(italic(&opt.argument)); - if let Some(ref default) = opt.default { - if !args.is_empty() { - args.push(" ".to_string()); - } - args.push("[".into()); - args.push("default:".into()); - args.push(" ".into()); - args.push(italic(&default)); - args.push("]".into()); - } - let desc = match opt.help { - Some(ref desc) => desc.to_string(), - None => "".to_string(), - }; - arr.push(list(&args, &[desc])); - - if index != last { - arr.push(format!("\n\n")); - } - } - page.section("OPTIONS", &arr) -} - -/// Create a `ENVIRONMENT` section. -/// -/// ## Formatting -/// ```txt -/// ENVIRONMENT -/// ``` -fn environment(page: Roff, environment: &[Env]) -> Roff { - if environment.is_empty() { - return page; - } - - let last = environment.len() - 1; - let mut arr: Vec = vec![]; - for (index, env) in environment.iter().enumerate() { - let mut args: Vec = vec![]; - args.push(bold(&env.name)); - if let Some(ref default) = env.default { - if !args.is_empty() { - args.push(" ".to_string()); - } - args.push("[".into()); - args.push("default:".into()); - args.push(" ".into()); - args.push(italic(&default)); - args.push("]".into()); - } - let desc = match env.help { - Some(ref desc) => desc.to_string(), - None => "".to_string(), - }; - arr.push(list(&args, &[desc])); - - if index != last { - arr.push(format!("\n\n")); - } - } - page.section("ENVIRONMENT", &arr) -} - -/// 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. -/// -/// ## Formatting -/// ```txt -/// EXIT STATUS -/// 0 Successful program execution -/// -/// 1 Usage, syntax or configuration file error -/// -/// 2 Optional error -/// ``` -fn exit_status(page: Roff) -> Roff { - page.section( - "EXIT STATUS", - &[list(&[bold("0")], &["Successful program execution."])], - ) -} - -// NOTE(yw): This code was taken from the npm-install(1) command. The location -// on your system may vary. In all honesty I just copy-pasted this. We should -// probably port this to troff-rs at some point. -// -// ```sh -// $ less /usr/share/man/man1/npm-install.1 -// ``` -fn init_list() -> String { - format!(".P\n.RS 2\n.nf\n") -} diff --git a/src/prelude.rs b/src/prelude.rs index 0c3eaca..42cbcd2 100644 --- a/src/prelude.rs +++ b/src/prelude.rs @@ -12,6 +12,7 @@ pub use arg::Arg; pub use author::Author; pub use environment::Env; +pub use exit_status::ExitStatus; pub use flag::Flag; pub use man::Manual; pub use option::Opt; From a09fcd70d4c3e979ff44ca309999f0acf6d4def2 Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Tue, 22 Jan 2019 13:30:23 -0500 Subject: [PATCH 2/7] Minor changes for readability --- src/man.rs | 26 ++++++++++++-------------- 1 file changed, 12 insertions(+), 14 deletions(-) diff --git a/src/man.rs b/src/man.rs index 4275042..9a153f0 100644 --- a/src/man.rs +++ b/src/man.rs @@ -360,26 +360,24 @@ fn exit_status(page: Roff, exit_statuses: &[ExitStatus]) -> Roff { if exit_statuses.is_empty() { return page; } - let arr = if exit_statuses - .iter() - .any(|status| status.use_default_instead) - { + let should_use_default = exit_statuses.iter().any(|s| s.use_default_instead); + let arr = if should_use_default { vec![ list(&[bold("0")], &["Successful program execution.\n\n"]), list(&[bold("1")], &["Unsuccessful program execution.\n\n"]), list(&[bold("101")], &["The program panicked."]), ] } else { - let mut arr = vec![]; - for exit_status in exit_statuses { - let exit_code = - format!("{}", exit_status.code.expect("initialized with value")); - let mut exit_description = - String::from(exit_status.description.unwrap_or("")); - exit_description.push_str("\n\n"); - arr.push(list(&[bold(&exit_code)], &[exit_description])); - } - arr + exit_statuses + .iter() + .map(|status| { + let code = + format!("{}", status.code.expect("initialized with a value")); + let mut description = String::from(status.description.unwrap_or("")); + description.push_str("\n\n"); + list(&[bold(&code)], &[description]) + }) + .collect() }; page.section("EXIT STATUS", &arr) } From 68c25788aa3a365dce7647070bdad4423bedbc92 Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Fri, 25 Jan 2019 19:52:39 -0500 Subject: [PATCH 3/7] Refactor to avoid sentential value --- examples/main.rs | 12 +++--- src/exit_status.rs | 10 ----- src/man.rs | 91 ++++++++++++++++++++++++++++++++++------------ 3 files changed, 73 insertions(+), 40 deletions(-) diff --git a/examples/main.rs b/examples/main.rs index 9abdbbb..5871697 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -31,6 +31,12 @@ fn main() { .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"), + ) + .exit_status(ExitStatus::default()) .example( Example::new() .text("listen on port 3000") @@ -43,12 +49,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"), - ) - .exit_status(ExitStatus::default()) .author(Author::new("Alice Person").email("alice@person.com")) .author(Author::new("Bob Human").email("bob@human.com")) .render(); diff --git a/src/exit_status.rs b/src/exit_status.rs index de78e45..db47937 100644 --- a/src/exit_status.rs +++ b/src/exit_status.rs @@ -3,7 +3,6 @@ pub struct ExitStatus { pub(crate) code: Option, pub(crate) description: Option<&'static str>, - pub(crate) use_default_instead: bool, } impl ExitStatus { @@ -11,15 +10,6 @@ impl ExitStatus { Self { code: Some(code), description: None, - use_default_instead: false, - } - } - - pub fn default() -> Self { - Self { - code: None, - description: None, - use_default_instead: true, } } diff --git a/src/man.rs b/src/man.rs index 9a153f0..d58860c 100644 --- a/src/man.rs +++ b/src/man.rs @@ -13,10 +13,45 @@ pub struct Manual { environment: Vec, arguments: Vec, custom_sections: Vec
, - exit_statuses: Vec, + exit_statuses: ExitStatuses, examples: Vec, } +#[derive(Debug, Clone)] +enum ExitStatuses { + DefaultStatuses([ExitStatus; 3]), + CustomStatuses(Vec), +} + +impl ExitStatuses { + fn push(&mut self, new_status: ExitStatus) { + match self.to_owned() { + ExitStatuses::CustomStatuses(mut vec) => { + vec.push(new_status); + *self = ExitStatuses::CustomStatuses(vec); + } + _ => {} + }; + } + + fn set_to_default(&mut self) { + *self = ExitStatuses::DefaultStatuses([ + 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 Manual { /// Create a new instance. pub fn new(name: &str) -> Self { @@ -30,7 +65,7 @@ impl Manual { arguments: vec![], environment: vec![], custom_sections: vec![], - exit_statuses: vec![], + exit_statuses: ExitStatuses::CustomStatuses(vec![]), examples: vec![], } } @@ -92,7 +127,14 @@ impl Manual { } pub fn exit_status(mut self, exit_status: ExitStatus) -> Self { - self.exit_statuses.push(exit_status); + match exit_status { + ExitStatus { code: None, .. } => { + self.exit_statuses.set_to_default(); + } + _ => { + self.exit_statuses.push(exit_status); + } + } self } @@ -356,30 +398,31 @@ fn env(page: Roff, environment: &[Env]) -> Roff { /// /// 2 Optional error /// ``` -fn exit_status(page: Roff, exit_statuses: &[ExitStatus]) -> Roff { - if exit_statuses.is_empty() { - return page; - } - let should_use_default = exit_statuses.iter().any(|s| s.use_default_instead); - let arr = if should_use_default { - vec![ - list(&[bold("0")], &["Successful program execution.\n\n"]), - list(&[bold("1")], &["Unsuccessful program execution.\n\n"]), - list(&[bold("101")], &["The program panicked."]), - ] - } else { - exit_statuses - .iter() - .map(|status| { +fn exit_status(page: Roff, exit_statuses: &ExitStatuses) -> Roff { + let mut arr = vec![]; + match exit_statuses { + ExitStatuses::DefaultStatuses(default_statuses) => { + for status in default_statuses.iter() { + let code = format!("{}", status.code.expect("Set as part of default")); + let mut description = String::from(status.description.unwrap()); + description.push_str("\n\n"); + arr.push(list(&[bold(&code)], &[description])); + } + } + ExitStatuses::CustomStatuses(custom_statuses) => { + if custom_statuses.is_empty() { + return page; + } + for status in custom_statuses.iter() { let code = - format!("{}", status.code.expect("initialized with a value")); + format!("{}", status.code.expect("Always set when not default")); let mut description = String::from(status.description.unwrap_or("")); description.push_str("\n\n"); - list(&[bold(&code)], &[description]) - }) - .collect() - }; - page.section("EXIT STATUS", &arr) + arr.push(list(&[bold(&code)], &[description])); + } + } + } + page.section("exit status", &arr) } /// Create a custom section. From d97d93aea80bbb2b88e00bce48a3fa71ed9fac68 Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Fri, 25 Jan 2019 20:04:27 -0500 Subject: [PATCH 4/7] Change `match` statement to `if let` statement Changed to make Clippy happy --- src/man.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) diff --git a/src/man.rs b/src/man.rs index d58860c..53eda6c 100644 --- a/src/man.rs +++ b/src/man.rs @@ -25,13 +25,10 @@ enum ExitStatuses { impl ExitStatuses { fn push(&mut self, new_status: ExitStatus) { - match self.to_owned() { - ExitStatuses::CustomStatuses(mut vec) => { - vec.push(new_status); - *self = ExitStatuses::CustomStatuses(vec); - } - _ => {} - }; + if let ExitStatuses::CustomStatuses(mut vec) = self.to_owned() { + vec.push(new_status); + *self = ExitStatuses::CustomStatuses(vec); + } } fn set_to_default(&mut self) { From 71444fcf2c8d101f5b46b13dbf658c5553c17b9a Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Sat, 26 Jan 2019 11:06:51 -0500 Subject: [PATCH 5/7] Add an iter method to the ExitStatuses enum --- examples/main.rs | 5 --- examples/without-defaults.rs | 67 ++++++++++++++++++++++++++++++++ src/exit_status.rs | 4 +- src/man.rs | 75 +++++++++++++++++------------------- 4 files changed, 105 insertions(+), 46 deletions(-) create mode 100644 examples/without-defaults.rs diff --git a/examples/main.rs b/examples/main.rs index 5871697..6f327da 100644 --- a/examples/main.rs +++ b/examples/main.rs @@ -31,11 +31,6 @@ fn main() { .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"), - ) .exit_status(ExitStatus::default()) .example( Example::new() diff --git a/examples/without-defaults.rs b/examples/without-defaults.rs new file mode 100644 index 0000000..3fbd43d --- /dev/null +++ b/examples/without-defaults.rs @@ -0,0 +1,67 @@ +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).description("Successful program execution."), + ) + .exit_status(ExitStatus::new(1).description("Invalid input.")) + .exit_status(ExitStatus::new(2).description("Could not read config file.")) + .exit_status(ExitStatus::new(3).description("Could not connect to sever.")) + .exit_status(ExitStatus::new(101).description("The program panicked.")) + .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 index db47937..7fd01e5 100644 --- a/src/exit_status.rs +++ b/src/exit_status.rs @@ -1,14 +1,14 @@ /// Add a exit status section #[derive(Debug, Clone, Default)] pub struct ExitStatus { - pub(crate) code: Option, + pub(crate) code: i32, pub(crate) description: Option<&'static str>, } impl ExitStatus { pub fn new(code: i32) -> Self { Self { - code: Some(code), + code: code, description: None, } } diff --git a/src/man.rs b/src/man.rs index 53eda6c..77b3370 100644 --- a/src/man.rs +++ b/src/man.rs @@ -19,33 +19,41 @@ pub struct Manual { #[derive(Debug, Clone)] enum ExitStatuses { - DefaultStatuses([ExitStatus; 3]), + DefaultStatuses, CustomStatuses(Vec), } +static DEFAULT_STATUSES: [ExitStatus; 3] = [ + ExitStatus { + code: 0, + description: Some("Successful program execution."), + }, + ExitStatus { + code: 1, + description: Some("Unsuccessful program execution."), + }, + ExitStatus { + code: 101, + description: Some("The program panicked."), + }, +]; + impl ExitStatuses { fn push(&mut self, new_status: ExitStatus) { - if let ExitStatuses::CustomStatuses(mut vec) = self.to_owned() { + if let ExitStatuses::CustomStatuses(vec) = self { vec.push(new_status); - *self = ExitStatuses::CustomStatuses(vec); } } fn set_to_default(&mut self) { - *self = ExitStatuses::DefaultStatuses([ - 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."), - }, - ]); + *self = ExitStatuses::DefaultStatuses; + } + + fn iter(&self) -> impl Iterator { + match self { + ExitStatuses::CustomStatuses(vec) => vec.iter(), + ExitStatuses::DefaultStatuses => DEFAULT_STATUSES.iter(), + } } } @@ -125,7 +133,9 @@ impl Manual { pub fn exit_status(mut self, exit_status: ExitStatus) -> Self { match exit_status { - ExitStatus { code: None, .. } => { + ExitStatus { + description: None, .. + } => { self.exit_statuses.set_to_default(); } _ => { @@ -396,28 +406,15 @@ fn env(page: Roff, environment: &[Env]) -> Roff { /// 2 Optional error /// ``` fn exit_status(page: Roff, exit_statuses: &ExitStatuses) -> Roff { + if exit_statuses.iter().collect::>().is_empty() { + return page; + } let mut arr = vec![]; - match exit_statuses { - ExitStatuses::DefaultStatuses(default_statuses) => { - for status in default_statuses.iter() { - let code = format!("{}", status.code.expect("Set as part of default")); - let mut description = String::from(status.description.unwrap()); - description.push_str("\n\n"); - arr.push(list(&[bold(&code)], &[description])); - } - } - ExitStatuses::CustomStatuses(custom_statuses) => { - if custom_statuses.is_empty() { - return page; - } - for status in custom_statuses.iter() { - let code = - format!("{}", status.code.expect("Always set when not default")); - let mut description = String::from(status.description.unwrap_or("")); - description.push_str("\n\n"); - arr.push(list(&[bold(&code)], &[description])); - } - } + for status in exit_statuses.iter() { + let code = format!("{}", status.code); + 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) } From 272a4131474f6f239dedf76ea917576270ff5e34 Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Sat, 26 Jan 2019 11:10:49 -0500 Subject: [PATCH 6/7] Clippy lints --- src/exit_status.rs | 2 +- src/man.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/exit_status.rs b/src/exit_status.rs index 7fd01e5..8bb4d79 100644 --- a/src/exit_status.rs +++ b/src/exit_status.rs @@ -8,7 +8,7 @@ pub struct ExitStatus { impl ExitStatus { pub fn new(code: i32) -> Self { Self { - code: code, + code, description: None, } } diff --git a/src/man.rs b/src/man.rs index 77b3370..4a8fada 100644 --- a/src/man.rs +++ b/src/man.rs @@ -406,7 +406,7 @@ fn env(page: Roff, environment: &[Env]) -> Roff { /// 2 Optional error /// ``` fn exit_status(page: Roff, exit_statuses: &ExitStatuses) -> Roff { - if exit_statuses.iter().collect::>().is_empty() { + if exit_statuses.iter().next().is_none() { return page; } let mut arr = vec![]; From 4be686020a74cea5d862065dfafdba31d18e1d54 Mon Sep 17 00:00:00 2001 From: Daniel Sockwell Date: Sat, 26 Jan 2019 11:32:50 -0500 Subject: [PATCH 7/7] Revert removal of Option wrapping `code` --- examples/without-defaults.rs | 8 +------- src/exit_status.rs | 4 ++-- src/man.rs | 12 +++++------- 3 files changed, 8 insertions(+), 16 deletions(-) diff --git a/examples/without-defaults.rs b/examples/without-defaults.rs index 3fbd43d..f604d7a 100644 --- a/examples/without-defaults.rs +++ b/examples/without-defaults.rs @@ -40,13 +40,7 @@ fn main() { .paragraph("text for the custom section") .paragraph("Additional text for the custom section"), ) - .exit_status( - ExitStatus::new(0).description("Successful program execution."), - ) - .exit_status(ExitStatus::new(1).description("Invalid input.")) - .exit_status(ExitStatus::new(2).description("Could not read config file.")) - .exit_status(ExitStatus::new(3).description("Could not connect to sever.")) - .exit_status(ExitStatus::new(101).description("The program panicked.")) + .exit_status(ExitStatus::new(0)) .example( Example::new() .text("listen on port 3000") diff --git a/src/exit_status.rs b/src/exit_status.rs index 8bb4d79..db47937 100644 --- a/src/exit_status.rs +++ b/src/exit_status.rs @@ -1,14 +1,14 @@ /// Add a exit status section #[derive(Debug, Clone, Default)] pub struct ExitStatus { - pub(crate) code: i32, + pub(crate) code: Option, pub(crate) description: Option<&'static str>, } impl ExitStatus { pub fn new(code: i32) -> Self { Self { - code, + code: Some(code), description: None, } } diff --git a/src/man.rs b/src/man.rs index 4a8fada..8784d82 100644 --- a/src/man.rs +++ b/src/man.rs @@ -25,15 +25,15 @@ enum ExitStatuses { static DEFAULT_STATUSES: [ExitStatus; 3] = [ ExitStatus { - code: 0, + code: Some(0), description: Some("Successful program execution."), }, ExitStatus { - code: 1, + code: Some(1), description: Some("Unsuccessful program execution."), }, ExitStatus { - code: 101, + code: Some(101), description: Some("The program panicked."), }, ]; @@ -133,9 +133,7 @@ impl Manual { pub fn exit_status(mut self, exit_status: ExitStatus) -> Self { match exit_status { - ExitStatus { - description: None, .. - } => { + ExitStatus { code: None, .. } => { self.exit_statuses.set_to_default(); } _ => { @@ -411,7 +409,7 @@ fn exit_status(page: Roff, exit_statuses: &ExitStatuses) -> Roff { } let mut arr = vec![]; for status in exit_statuses.iter() { - let code = format!("{}", status.code); + 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]));