diff --git a/argh/tests/lib.rs b/argh/tests/lib.rs index dbd3b48..0f3998a 100644 --- a/argh/tests/lib.rs +++ b/argh/tests/lib.rs @@ -303,6 +303,30 @@ Options: ); } +#[test] +fn escaped_doc_comment_description() { + #[derive(FromArgs)] + /// A \description\: + /// \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~\ + struct Cmd { + #[argh(switch)] + /// a \description\: + /// \!\"\#\$\%\&\'\(\)\*\+\,\-\.\/\:\;\<\=\>\?\@\[\\\]\^\_\`\{\|\}\~\ + _s: bool, + } + + assert_help_string::( + r###"Usage: test_arg_0 [--s] + +A \description: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\ + +Options: + --s a \description: !"#$%&'()*+,-./:;<=>?@[\]^_`{|}~\ + --help display usage information +"###, + ); +} + #[test] fn explicit_long_value_for_option() { #[derive(FromArgs, Debug)] diff --git a/argh_derive/src/parse_attrs.rs b/argh_derive/src/parse_attrs.rs index f968ca7..70af45e 100644 --- a/argh_derive/src/parse_attrs.rs +++ b/argh_derive/src/parse_attrs.rs @@ -500,7 +500,7 @@ fn parse_attr_doc(errors: &Errors, attr: &syn::Attribute, slot: &mut Option String { + let mut result = String::with_capacity(s.len()); + + let mut characters = s.chars().peekable(); + while let Some(mut character) = characters.next() { + if character == '\\' { + if let Some(next_character) = characters.peek() { + if next_character.is_ascii_punctuation() { + character = *next_character; + characters.next(); + } + } + } + + // Braces must be escaped as this string will be used as a format string + if character == '{' || character == '}' { + result.push(character); + } + + result.push(character); + } + + result +} + fn parse_attr_description(errors: &Errors, m: &syn::MetaNameValue, slot: &mut Option) { let lit_str = if let Some(lit_str) = errors.expect_lit_str(&m.value) { lit_str } else { return };