Repository: jacobdeichert/mask (language: rust)

Issue:
Using `<>` for required args causes breakage in certain markdown renderers
**Source:**
```md
### serve <port>
```

**Rendered:**
### serve <port>

---
**There's a temporary workaround: use an underscore in your arg name.**

**Source:**
```md
### serve <serve_port>
```

**Rendered:**
### serve <serve_port>

---

### Long-term solution

I was going to use `[]` for `[optional]` [args](https://github.com/jakedeichert/mask/issues/5). 

Maybe `[[required]]`? 

Or `<$required>` and `[$optional]`?

[Reported on twitter](https://twitter.com/JoelJPresent/status/1151821550900908032)
Using `inline code` in a command description doesn't get output with `-h`
When printing the help info for a command, inline code within a description is not printed.

~~~md
## cmdname
> A description with `inline code`
~~~



Discussion / hints:

Fix patch:
diff --git a/Cargo.toml b/Cargo.toml
index 42b26a7..58c320d 100644
--- a/Cargo.toml
+++ b/Cargo.toml
@@ -8,7 +8,7 @@ readme = "README.md"
 keywords = ["cli", "task", "command", "make", "markdown"]
 categories = ["command-line-interface", "command-line-utilities", "development-tools::build-utils", "parser-implementations"]
 edition = "2018"
-exclude = ["maskfile.md", ".gitignore", "CHANGELOG.md", "test/**"]
+exclude = ["maskfile.md", ".gitignore", "CHANGELOG.md", "test/**", ".circleci/**"]
 license = "MIT"
 
 [lib]
diff --git a/README.md b/README.md
index 1cc0528..31229e9 100644
--- a/README.md
+++ b/README.md
@@ -74,12 +74,12 @@ mask test
 
 ### Positional arguments
 
-These are defined beside the command name within `<angle_brackets>`. They are required arguments that must be supplied for the command to run. [Optional args][2] are coming soon. The argument name is injected into the script's scope as an environment variable.
+These are defined beside the command name within `(round_brackets)`. They are required arguments that must be supplied for the command to run. [Optional args][2] are coming soon. The argument name is injected into the script's scope as an environment variable.
 
 **Example:**
 
 ```markdown
-## test <file> <test_case>
+## test (file) (test_case)
 
 > Run tests
 
@@ -129,7 +129,7 @@ Nested command structures can easily be created since they are simply defined by
 
 > Commands related to starting, stopping, and restarting services
 
-### services start <service_name>
+### services start (service_name)
 
 > Start a service.
 
@@ -137,7 +137,7 @@ Nested command structures can easily be created since they are simply defined by
 echo "Starting service $service_name"
 ~~~
 
-### services stop <service_name>
+### services stop (service_name)
 
 > Stop a service.
 
@@ -161,7 +161,7 @@ On top of shell/bash scripts, `mask` also supports using node, python, ruby and
 **Example:**
 
 ```markdown
-## shell <name>
+## shell (name)
 
 > An example shell script
 
@@ -173,7 +173,7 @@ echo "Hello, $name!"
 ~~~
 
 
-## node <name>
+## node (name)
 
 > An example node script
 
@@ -181,11 +181,11 @@ Valid lang codes: js, javascript
 
 ~~~js
 const { name } = process.env;
-console.log(`Hello, ${name}!`)
+console.log(`Hello, ${name}!`);
 ~~~
 
 
-## python <name>
+## python (name)
 
 > An example python script
 
@@ -200,7 +200,7 @@ print("Hello, " + name + "!")
 ~~~
 
 
-## ruby <name>
+## ruby (name)
 
 > An example ruby script
 
@@ -213,7 +213,7 @@ puts "Hello, #{name}!"
 ~~~
 
 
-## php <name>
+## php (name)
 
 > An example php script
 
diff --git a/maskfile.md b/maskfile.md
index ca128c5..bf1c8b7 100644
--- a/maskfile.md
+++ b/maskfile.md
@@ -6,7 +6,7 @@
 
 
 
-## run <maskfile_command>
+## run (maskfile_command)
 
 > Build and run mask in development mode
 
diff --git a/src/parser.rs b/src/parser.rs
index c533ea8..9400ccd 100644
--- a/src/parser.rs
+++ b/src/parser.rs
@@ -1,5 +1,5 @@
 use pulldown_cmark::{
-    Event::{End, InlineHtml, Start, Text},
+    Event::{Code, End, InlineHtml, Start, Text},
     Options, Parser, Tag,
 };
 
@@ -112,6 +112,9 @@ pub fn build_command_structure(maskfile_contents: String) -> Command {
             InlineHtml(html) => {
                 text += &html.to_string();
             }
+            Code(inline_code) => {
+                text += &format!("`{}`", inline_code);
+            }
             _ => (),
         };
     }
@@ -183,8 +186,8 @@ fn parse_command_name_and_required_args(
     // and level 2 can't be a subcommand so no need to split.
     let name = if heading_level > 2 {
         // Takes a subcommand name like this:
-        // "#### db flush postgres <required_arg>"
-        // and returns "postgres <required_arg>" as the actual name
+        // "#### db flush postgres (required_arg_name)"
+        // and returns "postgres (required_arg_name)" as the actual name
         text.clone()
             .split(" ")
             .collect::<Vec<&str>>()
@@ -196,8 +199,8 @@ fn parse_command_name_and_required_args(
         text.clone()
     };
 
-    // Find any required arguments. They look like this: <required_arg_name>
-    let name_and_args: Vec<&str> = name.split(|c| c == '<' || c == '>').collect();
+    // Find any required arguments. They look like this: (required_arg_name)
+    let name_and_args: Vec<&str> = name.split(|c| c == '(' || c == ')').collect();
     let (name, args) = name_and_args.split_at(1);
     let name = name.join(" ").trim().to_string();
     let mut required_args: Vec<RequiredArg> = vec![];
@@ -215,3 +218,96 @@ fn parse_command_name_and_required_args(
 
     (name, required_args)
 }
+
+#[cfg(test)]
+const TEST_MASKFILE: &str = r#"
+# Document Title
+
+This is an example maskfile for the tests below.
+
+## serve (port)
+
+> Serve the app on the `port`
+
+~~~bash
+echo "Serving on port $port"
+~~~
+
+## node (name)
+
+> An example node script
+
+Valid lang codes: js, javascript
+
+```js
+const { name } = process.env;
+console.log(`Hello, ${name}!`);
+```
+"#;
+
+#[cfg(test)]
+mod build_command_structure {
+    use super::*;
+
+    #[test]
+    fn parses_serve_command_name() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let serve_command = &tree.subcommands[0];
+        assert_eq!(serve_command.name, "serve");
+    }
+
+    #[test]
+    fn parses_serve_command_description() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let serve_command = &tree.subcommands[0];
+        assert_eq!(serve_command.desc, "Serve the app on the `port`");
+    }
+
+    #[test]
+    fn parses_serve_required_positional_arguments() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let serve_command = &tree.subcommands[0];
+        assert_eq!(serve_command.required_args.len(), 1);
+        assert_eq!(serve_command.required_args[0].name, "port");
+    }
+
+    #[test]
+    fn adds_default_verbose_optional_flag() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let serve_command = &tree.subcommands[0];
+        assert_eq!(serve_command.option_flags.len(), 1);
+        assert_eq!(serve_command.option_flags[0].name, "verbose");
+        assert_eq!(
+            serve_command.option_flags[0].desc,
+            "Sets the level of verbosity"
+        );
+        assert_eq!(serve_command.option_flags[0].short, "v");
+        assert_eq!(serve_command.option_flags[0].long, "verbose");
+        assert_eq!(serve_command.option_flags[0].multiple, false);
+        assert_eq!(serve_command.option_flags[0].takes_value, false);
+    }
+
+    #[test]
+    fn parses_serve_command_executor() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let serve_command = &tree.subcommands[0];
+        assert_eq!(serve_command.executor, "bash");
+    }
+
+    #[test]
+    fn parses_serve_command_source_with_tildes() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let serve_command = &tree.subcommands[0];
+        assert_eq!(serve_command.source, "echo \"Serving on port $port\"\n");
+    }
+
+    #[test]
+    fn parses_node_command_source_with_backticks() {
+        let tree = build_command_structure(TEST_MASKFILE.to_string());
+        let node_command = &tree.subcommands[1];
+        assert_eq!(
+            node_command.source,
+            "const { name } = process.env;\nconsole.log(`Hello, ${name}!`);\n"
+        );
+    }
+}


Test patch:
diff --git a/test/maskfile.md b/test/maskfile.md
index 0628ea5..e74c3d4 100644
--- a/test/maskfile.md
+++ b/test/maskfile.md
@@ -11,7 +11,7 @@
 <!-- Cmd description goes here. -->
 > Commands related to starting, stopping, and restarting services
 
-### services start <service_name>
+### services start (service_name)
 
 > Start or restart a service.
 
@@ -20,7 +20,7 @@ echo "Starting service $service_name"
 ~~~
 
 
-### services stop <service_name>
+### services stop (service_name)
 
 > Stop a service.
 
@@ -65,7 +65,7 @@ echo "Flushed postgres"
 echo "Flushed redis"
 ~~~
 
-### db snapshot <snapshot_name>
+### db snapshot (snapshot_name)
 
 > Take a snapshot of the database.
 
@@ -73,7 +73,7 @@ echo "Flushed redis"
 echo "Saved db snapshot as '$snapshot_name'"
 ~~~
 
-### db restore <snapshot_name>
+### db restore (snapshot_name)
 
 > Restore the database to a snapshot.
 
@@ -111,7 +111,7 @@ server.listen(PORT, () => {
 
 
 
-## input <arg1> <arg2>
+## input (arg1) (arg2)
 
 > Example of how to accept user input and sleep
 
@@ -152,7 +152,7 @@ fi
 
 
 
-## python <name>
+## python (name)
 
 > An example python script
 
@@ -168,7 +168,7 @@ print("Hello, " + name + "!")
 
 
 
-## ruby <name>
+## ruby (name)
 
 > An example ruby script
 
@@ -182,7 +182,7 @@ puts "Hello, #{name}!"
 
 
 
-## php <name>
+## php (name)
 
 > An example php script