diff --git a/embassy/demos/common/src/menu.rs b/embassy/demos/common/src/menu.rs
new file mode 100644
index 0000000000000000000000000000000000000000..4720d003334e53e43ee4fdc101e5cd6a2fb3f023
--- /dev/null
+++ b/embassy/demos/common/src/menu.rs
@@ -0,0 +1,844 @@
+// This file was originally copied from "menu" crate by Jonathan Pallant
+// licensed under MIT or Apache-2.0
+// 5c0c0fd37608b8ae9e55b156c40bc740066b4d03
+
+//! # Menu
+//!
+//! A basic command-line interface for `#![no_std]` Rust programs. Peforms
+//! zero heap allocation.
+#![no_std]
+#![deny(missing_docs)]
+
+/// The type of function we call when we enter/exit a menu.
+pub type MenuCallbackFn<T> = fn(menu: &Menu<T>, context: &mut T);
+
+/// The type of function we call when we a valid command has been entered.
+pub type ItemCallbackFn<T> = fn(menu: &Menu<T>, item: &Item<T>, args: &[&str], context: &mut T);
+
+#[derive(Debug)]
+/// Describes a parameter to the command
+pub enum Parameter<'a> {
+    /// A mandatory positional parameter
+    Mandatory {
+        /// A name for this mandatory positional parameter
+        parameter_name: &'a str,
+        /// Help text
+        help: Option<&'a str>,
+    },
+    /// An optional positional parameter. Must come after the mandatory positional arguments.
+    Optional {
+        /// A name for this optional positional parameter
+        parameter_name: &'a str,
+        /// Help text
+        help: Option<&'a str>,
+    },
+    /// An optional named parameter with no argument (e.g. `--verbose` or `--dry-run`)
+    Named {
+        /// The bit that comes after the `--`
+        parameter_name: &'a str,
+        /// Help text
+        help: Option<&'a str>,
+    },
+    /// A optional named parameter with argument (e.g. `--mode=foo` or `--level=3`)
+    NamedValue {
+        /// The bit that comes after the `--`
+        parameter_name: &'a str,
+        /// The bit that comes after the `--name=`, e.g. `INT` or `FILE`. It's mostly for help text.
+        argument_name: &'a str,
+        /// Help text
+        help: Option<&'a str>,
+    },
+}
+
+/// Do we enter a sub-menu when this command is entered, or call a specific
+/// function?
+pub enum ItemType<'a, T>
+where
+    T: 'a,
+{
+    /// Call a function when this command is entered
+    Callback {
+        /// The function to call
+        function: ItemCallbackFn<T>,
+        /// The list of parameters for this function. Pass an empty list if there aren't any.
+        parameters: &'a [Parameter<'a>],
+    },
+    /// This item is a sub-menu you can enter
+    Menu(&'a Menu<'a, T>),
+    /// Internal use only - do not use
+    _Dummy,
+}
+
+/// An `Item` is a what our menus are made from. Each item has a `name` which
+/// you have to enter to select this item. Each item can also have zero or
+/// more parameters, and some optional help text.
+pub struct Item<'a, T>
+where
+    T: 'a,
+{
+    /// The word you need to enter to activate this item. It is recommended
+    /// that you avoid whitespace in this string.
+    pub command: &'a str,
+    /// Optional help text. Printed if you enter `help`.
+    pub help: Option<&'a str>,
+    /// The type of this item - menu, callback, etc.
+    pub item_type: ItemType<'a, T>,
+}
+
+/// A `Menu` is made of one or more `Item`s.
+pub struct Menu<'a, T>
+where
+    T: 'a,
+{
+    /// Each menu has a label which is visible in the prompt, unless you are
+    /// the root menu.
+    pub label: &'a str,
+    /// A slice of menu items in this menu.
+    pub items: &'a [&'a Item<'a, T>],
+    /// A function to call when this menu is entered. If this is the root menu, this is called when the runner is created.
+    pub entry: Option<MenuCallbackFn<T>>,
+    /// A function to call when this menu is exited. Never called for the root menu.
+    pub exit: Option<MenuCallbackFn<T>>,
+}
+
+/// This structure handles the menu. You feed it bytes as they are read from
+/// the console and it executes menu actions when commands are typed in
+/// (followed by Enter).
+pub struct Runner<'a, T>
+where
+    T: core::fmt::Write,
+    T: 'a,
+{
+    buffer: &'a mut [u8],
+    used: usize,
+    /// Maximum four levels deep
+    menus: [Option<&'a Menu<'a, T>>; 4],
+    depth: usize,
+    /// The context object the `Runner` carries around.
+    pub context: T,
+}
+
+/// Looks for the named parameter in the parameter list of the item, then
+/// finds the correct argument.
+///
+/// * Returns `Ok(None)` if `parameter_name` gives an optional or named
+///   parameter and that argument was not given.
+/// * Returns `Ok(arg)` if the argument corresponding to `parameter_name` was
+///   found. `arg` is the empty string if the parameter was `Parameter::Named`
+///   (and hence doesn't take a value).
+/// * Returns `Err(())` if `parameter_name` was not in `item.parameter_list`
+///   or `item` wasn't an Item::Callback
+pub fn argument_finder<'a, T>(
+    item: &'a Item<'a, T>,
+    argument_list: &'a [&'a str],
+    name_to_find: &'a str,
+) -> Result<Option<&'a str>, ()> {
+    if let ItemType::Callback { parameters, .. } = item.item_type {
+        // Step 1 - Find `name_to_find` in the parameter list.
+        let mut found_param = None;
+        let mut mandatory_count = 0;
+        let mut optional_count = 0;
+        for param in parameters.iter() {
+            match param {
+                Parameter::Mandatory { parameter_name, .. } => {
+                    mandatory_count += 1;
+                    if *parameter_name == name_to_find {
+                        found_param = Some((param, mandatory_count));
+                    }
+                }
+                Parameter::Optional { parameter_name, .. } => {
+                    optional_count += 1;
+                    if *parameter_name == name_to_find {
+                        found_param = Some((param, optional_count));
+                    }
+                }
+                Parameter::Named { parameter_name, .. } => {
+                    if *parameter_name == name_to_find {
+                        found_param = Some((param, 0));
+                    }
+                }
+                Parameter::NamedValue { parameter_name, .. } => {
+                    if *parameter_name == name_to_find {
+                        found_param = Some((param, 0));
+                    }
+                }
+            }
+        }
+        // Step 2 - What sort of parameter is it?
+        match found_param {
+            // Step 2a - Mandatory Positional
+            Some((Parameter::Mandatory { .. }, mandatory_idx)) => {
+                // We want positional parameter number `mandatory_idx`.
+                let mut positional_args_seen = 0;
+                for arg in argument_list.iter().filter(|x| !x.starts_with("--")) {
+                    // Positional
+                    positional_args_seen += 1;
+                    if positional_args_seen == mandatory_idx {
+                        return Ok(Some(arg));
+                    }
+                }
+                // Valid thing to ask for but we don't have it
+                Ok(None)
+            }
+            // Step 2b - Optional Positional
+            Some((Parameter::Optional { .. }, optional_idx)) => {
+                // We want positional parameter number `mandatory_count + optional_idx`.
+                let mut positional_args_seen = 0;
+                for arg in argument_list.iter().filter(|x| !x.starts_with("--")) {
+                    // Positional
+                    positional_args_seen += 1;
+                    if positional_args_seen == (mandatory_count + optional_idx) {
+                        return Ok(Some(arg));
+                    }
+                }
+                // Valid thing to ask for but we don't have it
+                Ok(None)
+            }
+            // Step 2c - Named (e.g. `--verbose`)
+            Some((Parameter::Named { parameter_name, .. }, _)) => {
+                for arg in argument_list {
+                    if arg.starts_with("--") && (&arg[2..] == *parameter_name) {
+                        return Ok(Some(""));
+                    }
+                }
+                // Valid thing to ask for but we don't have it
+                Ok(None)
+            }
+            // Step 2d - NamedValue (e.g. `--level=123`)
+            Some((Parameter::NamedValue { parameter_name, .. }, _)) => {
+                let name_start = 2;
+                let equals_start = name_start + parameter_name.len();
+                let value_start = equals_start + 1;
+                for arg in argument_list {
+                    if arg.starts_with("--")
+                        && (arg.len() >= value_start)
+                        && (arg.get(equals_start..=equals_start) == Some("="))
+                        && (arg.get(name_start..equals_start) == Some(*parameter_name))
+                    {
+                        return Ok(Some(&arg[value_start..]));
+                    }
+                }
+                // Valid thing to ask for but we don't have it
+                Ok(None)
+            }
+            // Step 2e - not found
+            _ => Err(()),
+        }
+    } else {
+        // Not an item with arguments
+        Err(())
+    }
+}
+
+enum Outcome {
+    CommandProcessed,
+    NeedMore,
+}
+
+impl<'a, T> Runner<'a, T>
+where
+    T: core::fmt::Write,
+{
+    /// Create a new `Runner`. You need to supply a top-level menu, and a
+    /// buffer that the `Runner` can use. Feel free to pass anything as the
+    /// `context` type - the only requirement is that the `Runner` can
+    /// `write!` to the context, which it will do for all text output.
+    pub fn new(menu: &'a Menu<'a, T>, buffer: &'a mut [u8], mut context: T) -> Runner<'a, T> {
+        if let Some(cb_fn) = menu.entry {
+            cb_fn(menu, &mut context);
+        }
+        let mut r = Runner {
+            menus: [Some(menu), None, None, None],
+            depth: 0,
+            buffer,
+            used: 0,
+            context,
+        };
+        r.prompt(true);
+        r
+    }
+
+    /// Print out a new command prompt, including sub-menu names if
+    /// applicable.
+    pub fn prompt(&mut self, newline: bool) {
+        if newline {
+            writeln!(self.context).unwrap();
+        }
+        if self.depth != 0 {
+            let mut depth = 1;
+            while depth <= self.depth {
+                if depth > 1 {
+                    write!(self.context, "/").unwrap();
+                }
+                write!(self.context, "/{}", self.menus[depth].unwrap().label).unwrap();
+                depth += 1;
+            }
+        }
+        write!(self.context, "> ").unwrap();
+    }
+
+    /// Add a byte to the menu runner's buffer. If this byte is a
+    /// carriage-return, the buffer is scanned and the appropriate action
+    /// performed.
+    /// By default, an echo feature is enabled to display commands on the terminal.
+    pub fn input_byte(&mut self, input: u8) {
+        // Strip carriage returns
+        if input == 0x0A {
+            return;
+        }
+        let outcome = if input == 0x0D {
+            #[cfg(not(feature = "echo"))]
+            {
+                // Echo the command
+                write!(self.context, "\r").unwrap();
+                if let Ok(s) = core::str::from_utf8(&self.buffer[0..self.used]) {
+                    write!(self.context, "{}", s).unwrap();
+                }
+            }
+            // Handle the command
+            self.process_command();
+            Outcome::CommandProcessed
+        } else if (input == 0x08) || (input == 0x7F) {
+            // Handling backspace or delete
+            if self.used > 0 {
+                write!(self.context, "\u{0008} \u{0008}").unwrap();
+                self.used -= 1;
+            }
+            Outcome::NeedMore
+        } else if self.used < self.buffer.len() {
+            self.buffer[self.used] = input;
+            self.used += 1;
+
+            #[cfg(feature = "echo")]
+            {
+                // We have to do this song and dance because `self.prompt()` needs
+                // a mutable reference to self, and we can't have that while
+                // holding a reference to the buffer at the same time.
+                // This line grabs the buffer, checks it's OK, then releases it again
+                let valid = core::str::from_utf8(&self.buffer[0..self.used]).is_ok();
+                // Now we've released the buffer, we can draw the prompt
+                if valid {
+                    write!(self.context, "\r").unwrap();
+                    self.prompt(false);
+                }
+                // Grab the buffer again to render it to the screen
+                if let Ok(s) = core::str::from_utf8(&self.buffer[0..self.used]) {
+                    write!(self.context, "{}", s).unwrap();
+                }
+            }
+            Outcome::NeedMore
+        } else {
+            writeln!(self.context, "Buffer overflow!").unwrap();
+            Outcome::NeedMore
+        };
+        match outcome {
+            Outcome::CommandProcessed => {
+                self.used = 0;
+                self.prompt(true);
+            }
+            Outcome::NeedMore => {}
+        }
+    }
+
+    /// Scan the buffer and do the right thing based on its contents.
+    fn process_command(&mut self) {
+        // Go to the next line, below the prompt
+        writeln!(self.context).unwrap();
+        if let Ok(command_line) = core::str::from_utf8(&self.buffer[0..self.used]) {
+            // We have a valid string
+            let mut parts = command_line.split_whitespace();
+            if let Some(cmd) = parts.next() {
+                let menu = self.menus[self.depth].unwrap();
+                if cmd == "help" {
+                    match parts.next() {
+                        Some(arg) => match menu.items.iter().find(|i| i.command == arg) {
+                            Some(item) => {
+                                self.print_long_help(&item);
+                            }
+                            None => {
+                                writeln!(self.context, "I can't help with {:?}", arg).unwrap();
+                            }
+                        },
+                        _ => {
+                            writeln!(self.context, "AVAILABLE ITEMS:").unwrap();
+                            for item in menu.items {
+                                self.print_short_help(&item);
+                            }
+                            if self.depth != 0 {
+                                self.print_short_help(&Item {
+                                    command: "exit",
+                                    help: Some("Leave this menu."),
+                                    item_type: ItemType::_Dummy,
+                                });
+                            }
+                            self.print_short_help(&Item {
+                                command: "help [ <command> ]",
+                                help: Some("Show this help, or get help on a specific command."),
+                                item_type: ItemType::_Dummy,
+                            });
+                        }
+                    }
+                } else if cmd == "exit" && self.depth != 0 {
+                    self.menus[self.depth] = None;
+                    self.depth -= 1;
+                } else {
+                    let mut found = false;
+                    for item in menu.items {
+                        if cmd == item.command {
+                            match item.item_type {
+                                ItemType::Callback {
+                                    function,
+                                    parameters,
+                                } => Self::call_function(
+                                    &mut self.context,
+                                    function,
+                                    parameters,
+                                    menu,
+                                    item,
+                                    command_line,
+                                ),
+                                ItemType::Menu(m) => {
+                                    self.depth += 1;
+                                    self.menus[self.depth] = Some(m);
+                                }
+                                ItemType::_Dummy => {
+                                    unreachable!();
+                                }
+                            }
+                            found = true;
+                            break;
+                        }
+                    }
+                    if !found {
+                        writeln!(self.context, "Command {:?} not found. Try 'help'.", cmd).unwrap();
+                    }
+                }
+            } else {
+                writeln!(self.context, "Input was empty?").unwrap();
+            }
+        } else {
+            // Hmm ..  we did not have a valid string
+            writeln!(self.context, "Input was not valid UTF-8").unwrap();
+        }
+    }
+
+    fn print_short_help(&mut self, item: &Item<T>) {
+        let mut has_options = false;
+        match item.item_type {
+            ItemType::Callback { parameters, .. } => {
+                write!(self.context, "  {}", item.command).unwrap();
+                if !parameters.is_empty() {
+                    for param in parameters.iter() {
+                        match param {
+                            Parameter::Mandatory { parameter_name, .. } => {
+                                write!(self.context, " <{}>", parameter_name).unwrap();
+                            }
+                            Parameter::Optional { parameter_name, .. } => {
+                                write!(self.context, " [ <{}> ]", parameter_name).unwrap();
+                            }
+                            Parameter::Named { .. } => {
+                                has_options = true;
+                            }
+                            Parameter::NamedValue { .. } => {
+                                has_options = true;
+                            }
+                        }
+                    }
+                }
+            }
+            ItemType::Menu(_menu) => {
+                write!(self.context, "  {}", item.command).unwrap();
+            }
+            ItemType::_Dummy => {
+                write!(self.context, "  {}", item.command).unwrap();
+            }
+        }
+        if has_options {
+            write!(self.context, " [OPTIONS...]").unwrap();
+        }
+        writeln!(self.context).unwrap();
+    }
+
+    fn print_long_help(&mut self, item: &Item<T>) {
+        writeln!(self.context, "SUMMARY:").unwrap();
+        match item.item_type {
+            ItemType::Callback { parameters, .. } => {
+                write!(self.context, "  {}", item.command).unwrap();
+                if !parameters.is_empty() {
+                    for param in parameters.iter() {
+                        match param {
+                            Parameter::Mandatory { parameter_name, .. } => {
+                                write!(self.context, " <{}>", parameter_name).unwrap();
+                            }
+                            Parameter::Optional { parameter_name, .. } => {
+                                write!(self.context, " [ <{}> ]", parameter_name).unwrap();
+                            }
+                            Parameter::Named { parameter_name, .. } => {
+                                write!(self.context, " [ --{} ]", parameter_name).unwrap();
+                            }
+                            Parameter::NamedValue {
+                                parameter_name,
+                                argument_name,
+                                ..
+                            } => {
+                                write!(self.context, " [ --{}={} ]", parameter_name, argument_name)
+                                    .unwrap();
+                            }
+                        }
+                    }
+                    writeln!(self.context, "\n\nPARAMETERS:").unwrap();
+                    let default_help = "Undocumented option";
+                    for param in parameters.iter() {
+                        match param {
+                            Parameter::Mandatory {
+                                parameter_name,
+                                help,
+                            } => {
+                                writeln!(
+                                    self.context,
+                                    "  <{0}>\n    {1}\n",
+                                    parameter_name,
+                                    help.unwrap_or(default_help),
+                                )
+                                .unwrap();
+                            }
+                            Parameter::Optional {
+                                parameter_name,
+                                help,
+                            } => {
+                                writeln!(
+                                    self.context,
+                                    "  <{0}>\n    {1}\n",
+                                    parameter_name,
+                                    help.unwrap_or(default_help),
+                                )
+                                .unwrap();
+                            }
+                            Parameter::Named {
+                                parameter_name,
+                                help,
+                            } => {
+                                writeln!(
+                                    self.context,
+                                    "  --{0}\n    {1}\n",
+                                    parameter_name,
+                                    help.unwrap_or(default_help),
+                                )
+                                .unwrap();
+                            }
+                            Parameter::NamedValue {
+                                parameter_name,
+                                argument_name,
+                                help,
+                            } => {
+                                writeln!(
+                                    self.context,
+                                    "  --{0}={1}\n    {2}\n",
+                                    parameter_name,
+                                    argument_name,
+                                    help.unwrap_or(default_help),
+                                )
+                                .unwrap();
+                            }
+                        }
+                    }
+                }
+            }
+            ItemType::Menu(_menu) => {
+                write!(self.context, "  {}", item.command).unwrap();
+            }
+            ItemType::_Dummy => {
+                write!(self.context, "  {}", item.command).unwrap();
+            }
+        }
+        if let Some(help) = item.help {
+            writeln!(self.context, "\n\nDESCRIPTION:\n{}", help).unwrap();
+        }
+    }
+
+    fn call_function(
+        context: &mut T,
+        callback_function: ItemCallbackFn<T>,
+        parameters: &[Parameter],
+        parent_menu: &Menu<T>,
+        item: &Item<T>,
+        command: &str,
+    ) {
+        let mandatory_parameter_count = parameters
+            .iter()
+            .filter(|p| match p {
+                Parameter::Mandatory { .. } => true,
+                _ => false,
+            })
+            .count();
+        let positional_parameter_count = parameters
+            .iter()
+            .filter(|p| match p {
+                Parameter::Mandatory { .. } => true,
+                Parameter::Optional { .. } => true,
+                _ => false,
+            })
+            .count();
+        if command.len() >= item.command.len() {
+            // Maybe arguments
+            let mut argument_buffer: [&str; 16] = [""; 16];
+            let mut argument_count = 0;
+            let mut positional_arguments = 0;
+            for (slot, arg) in argument_buffer
+                .iter_mut()
+                .zip(command[item.command.len()..].split_whitespace())
+            {
+                *slot = arg;
+                argument_count += 1;
+                if arg.starts_with("--") {
+                    // Validate named argument
+                    let mut found = false;
+                    for param in parameters.iter() {
+                        match param {
+                            Parameter::Named { parameter_name, .. } => {
+                                if &arg[2..] == *parameter_name {
+                                    found = true;
+                                    break;
+                                }
+                            }
+                            Parameter::NamedValue { parameter_name, .. } => {
+                                if arg.contains('=') {
+                                    if let Some(given_name) = arg[2..].split('=').next() {
+                                        if given_name == *parameter_name {
+                                            found = true;
+                                            break;
+                                        }
+                                    }
+                                }
+                            }
+                            _ => {
+                                // Ignore
+                            }
+                        }
+                    }
+                    if !found {
+                        writeln!(context, "Error: Did not understand {:?}", arg).unwrap();
+                        return;
+                    }
+                } else {
+                    positional_arguments += 1;
+                }
+            }
+            if positional_arguments < mandatory_parameter_count {
+                writeln!(context, "Error: Insufficient arguments given").unwrap();
+            } else if positional_arguments > positional_parameter_count {
+                writeln!(context, "Error: Too many arguments given").unwrap();
+            } else {
+                callback_function(
+                    parent_menu,
+                    item,
+                    &argument_buffer[0..argument_count],
+                    context,
+                );
+            }
+        } else {
+            // Definitely no arguments
+            if mandatory_parameter_count == 0 {
+                callback_function(parent_menu, item, &[], context);
+            } else {
+                writeln!(context, "Error: Insufficient arguments given").unwrap();
+            }
+        }
+    }
+}
+
+#[cfg(test)]
+mod tests {
+    use super::*;
+
+    fn dummy(_menu: &Menu<u32>, _item: &Item<u32>, _args: &[&str], _context: &mut u32) {}
+
+    #[test]
+    fn find_arg_mandatory() {
+        let item = Item {
+            command: "dummy",
+            help: None,
+            item_type: ItemType::Callback {
+                function: dummy,
+                parameters: &[
+                    Parameter::Mandatory {
+                        parameter_name: "foo",
+                        help: Some("Some help for foo"),
+                    },
+                    Parameter::Mandatory {
+                        parameter_name: "bar",
+                        help: Some("Some help for bar"),
+                    },
+                    Parameter::Mandatory {
+                        parameter_name: "baz",
+                        help: Some("Some help for baz"),
+                    },
+                ],
+            },
+        };
+        assert_eq!(
+            argument_finder(&item, &["a", "b", "c"], "foo"),
+            Ok(Some("a"))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "b", "c"], "bar"),
+            Ok(Some("b"))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "b", "c"], "baz"),
+            Ok(Some("c"))
+        );
+        // Not an argument
+        assert_eq!(argument_finder(&item, &["a", "b", "c"], "quux"), Err(()));
+    }
+
+    #[test]
+    fn find_arg_optional() {
+        let item = Item {
+            command: "dummy",
+            help: None,
+            item_type: ItemType::Callback {
+                function: dummy,
+                parameters: &[
+                    Parameter::Mandatory {
+                        parameter_name: "foo",
+                        help: Some("Some help for foo"),
+                    },
+                    Parameter::Mandatory {
+                        parameter_name: "bar",
+                        help: Some("Some help for bar"),
+                    },
+                    Parameter::Optional {
+                        parameter_name: "baz",
+                        help: Some("Some help for baz"),
+                    },
+                ],
+            },
+        };
+        assert_eq!(
+            argument_finder(&item, &["a", "b", "c"], "foo"),
+            Ok(Some("a"))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "b", "c"], "bar"),
+            Ok(Some("b"))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "b", "c"], "baz"),
+            Ok(Some("c"))
+        );
+        // Not an argument
+        assert_eq!(argument_finder(&item, &["a", "b", "c"], "quux"), Err(()));
+        // Missing optional
+        assert_eq!(argument_finder(&item, &["a", "b"], "baz"), Ok(None));
+    }
+
+    #[test]
+    fn find_arg_named() {
+        let item = Item {
+            command: "dummy",
+            help: None,
+            item_type: ItemType::Callback {
+                function: dummy,
+                parameters: &[
+                    Parameter::Mandatory {
+                        parameter_name: "foo",
+                        help: Some("Some help for foo"),
+                    },
+                    Parameter::Named {
+                        parameter_name: "bar",
+                        help: Some("Some help for bar"),
+                    },
+                    Parameter::Named {
+                        parameter_name: "baz",
+                        help: Some("Some help for baz"),
+                    },
+                ],
+            },
+        };
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "foo"),
+            Ok(Some("a"))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "bar"),
+            Ok(Some(""))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "baz"),
+            Ok(Some(""))
+        );
+        // Not an argument
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "quux"),
+            Err(())
+        );
+        // Missing named
+        assert_eq!(argument_finder(&item, &["a"], "baz"), Ok(None));
+    }
+
+    #[test]
+    fn find_arg_namedvalue() {
+        let item = Item {
+            command: "dummy",
+            help: None,
+            item_type: ItemType::Callback {
+                function: dummy,
+                parameters: &[
+                    Parameter::Mandatory {
+                        parameter_name: "foo",
+                        help: Some("Some help for foo"),
+                    },
+                    Parameter::Named {
+                        parameter_name: "bar",
+                        help: Some("Some help for bar"),
+                    },
+                    Parameter::NamedValue {
+                        parameter_name: "baz",
+                        argument_name: "TEST",
+                        help: Some("Some help for baz"),
+                    },
+                ],
+            },
+        };
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "foo"),
+            Ok(Some("a"))
+        );
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "bar"),
+            Ok(Some(""))
+        );
+        // No argument so mark as not found
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "baz"),
+            Ok(None)
+        );
+        // Empty argument
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz="], "baz"),
+            Ok(Some(""))
+        );
+        // Short argument
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz=1"], "baz"),
+            Ok(Some("1"))
+        );
+        // Long argument
+        assert_eq!(
+            argument_finder(
+                &item,
+                &["a", "--bar", "--baz=abcdefghijklmnopqrstuvwxyz"],
+                "baz"
+            ),
+            Ok(Some("abcdefghijklmnopqrstuvwxyz"))
+        );
+        // Not an argument
+        assert_eq!(
+            argument_finder(&item, &["a", "--bar", "--baz"], "quux"),
+            Err(())
+        );
+        // Missing named
+        assert_eq!(argument_finder(&item, &["a"], "baz"), Ok(None));
+    }
+}