rust-embedded-community / menu

Command-line menu system for embedded Rust platforms.
Apache License 2.0
51 stars 11 forks source link

Menu

Introduction

A simple command-line menu system in Rust. Works on embedded systems, but also on your command-line.

NOTE: This crates works only in &str - there's no heap allocation, but there's also no automatic conversion to integers, boolean, etc.

user@host: ~/menu $ cargo run --example simple
   Compiling menu v0.5.0 (file:///home/user/menu)
    Finished dev [unoptimized + debuginfo] target(s) in 0.84 secs
     Running `target/debug/examples/simple`
In enter_root()
> help
AVAILABLE ITEMS:
  foo <a> [ <b> ] [ OPTIONS... ]
  bar
  sub
  help [ <command> ]

> help foo
SUMMARY:
  foo <a> [ <b> ] [ --verbose ] [ --level=INT ]

PARAMETERS:
  <a>
       This is the help text for 'a'
  <b>
       No help text found
  --verbose
       No help text found
  --level=INT
       Set the level of the dangle

DESCRIPTION:
Makes a foo appear.

This is some extensive help text.

It contains multiple paragraphs and should be preceeded by the parameter list.

> foo --level=3 --verbose 1 2
In select_foo. Args = ["--level=3", "--verbose", "1", "2"]
a = Ok(Some("1"))
b = Ok(Some("2"))
verbose = Ok(Some(""))
level = Ok(Some("3"))
no_such_arg = Err(())

> foo
Error: Insufficient arguments given!

> foo 1 2 3 3
Error: Too many arguments given

> sub

/sub> help
AVAILABLE ITEMS:
  baz
  quux
  exit
  help [ <command> ]

> exit

> help
AVAILABLE ITEMS:
  foo <a> [ <b> ] [ OPTIONS... ]
  bar
  sub
  help [ <command> ]

> ^C
user@host: ~/menu $

Using

See examples/simple.rs for a working example that runs on Linux or Windows. Here's the menu definition from that example:

const ROOT_MENU: Menu<Output> = Menu {
    label: "root",
    items: &[
        &Item {
            item_type: ItemType::Callback {
                function: select_foo,
                parameters: &[
                    Parameter::Mandatory {
                        parameter_name: "a",
                        help: Some("This is the help text for 'a'"),
                    },
                    Parameter::Optional {
                        parameter_name: "b",
                        help: None,
                    },
                    Parameter::Named {
                        parameter_name: "verbose",
                        help: None,
                    },
                    Parameter::NamedValue {
                        parameter_name: "level",
                        argument_name: "INT",
                        help: Some("Set the level of the dangle"),
                    },
                ],
            },
            command: "foo",
            help: Some(
                "Makes a foo appear.

This is some extensive help text.

It contains multiple paragraphs and should be preceeded by the parameter list.
",
            ),
        },
        &Item {
            item_type: ItemType::Callback {
                function: select_bar,
                parameters: &[],
            },
            command: "bar",
            help: Some("fandoggles a bar"),
        },
        &Item {
            item_type: ItemType::Menu(&Menu {
                label: "sub",
                items: &[
                    &Item {
                        item_type: ItemType::Callback {
                            function: select_baz,
                            parameters: &[],
                        },
                        command: "baz",
                        help: Some("thingamobob a baz"),
                    },
                    &Item {
                        item_type: ItemType::Callback {
                            function: select_quux,
                            parameters: &[],
                        },
                        command: "quux",
                        help: Some("maximum quux"),
                    },
                ],
                entry: Some(enter_sub),
                exit: Some(exit_sub),
            }),
            command: "sub",
            help: Some("enter sub-menu"),
        },
    ],
    entry: Some(enter_root),
    exit: Some(exit_root),
};

Using with noline

The noline crate is a no-std line-editor with history buffer. You can create a Runner using a noline::Editor instead of a raw byte slice, and then you will get a pub fn input_line(&mut self, context: &mut T) -> Result<(), NolineError> instead of the pub fn input_byte(&mut self, input: u8, context: &mut T) you get when you pass a mutable byte slice and let menu do the input handling. Call input_line and it will use the noline::Editor to read a line of text from the user, with history (press 'Up'!) and other nice features.

We chose this option as noline offers a bunch of benefits, but we didn't want to break everything for people who were quite happy with the basic line editing we have already.

See examples/noline.rs for an example.

Changelog

See CHANGELOG.md.

License

The contents of this repository are dual-licensed under the MIT OR Apache 2.0 License. That means you can choose either the MIT license or the Apache 2.0 license when you re-use this code. See LICENSE-MIT or LICENSE-APACHE for more information on each specific license. Our Apache 2.0 notices can be found in NOTICE.

Unless you explicitly state otherwise, any contribution intentionally submitted for inclusion in the work by you, as defined in the Apache-2.0 license, shall be dual licensed as above, without any additional terms or conditions.