1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
//! #CMDR
//!
//! **Cmdr is a library for building line-oriented text-based user interfaces.**
//!
//! This can be done by implementing one or more objects that implement the Cmdr::Scope trait. These
//! Scope objects can then be run by the Cmdr::cmd_loop executing commands.
//!
//! Implementing the scope trait can be done by hand by implementing the command method and
//! optionally overriding other methods to provide additional functionality. Or you can implement
//! leave the Scope trait up to us and just use the cmdr macro to do the heavy lifting.
//!
//! Any scope implements one or more command methods. Command methods names look like this:
//! ```do_<command>``` where the do_ prefix makes sure the cmdr macro recognizes the function as a
//! command and <command> will be how a user will invoke a command. Command methods take a mutable
//! reference to self (the scope) so they can change the scope when needed, a vector of &str
//! containing any parameters passed on the command line. And they return a CommandResult. This
//! allows the command to specify any follow-up actions to be performed (like quitting the program
//! for example).
//!

use std::io::stdout;
use std::io::stdin;
use std::io::Write;

pub use cmdr_macro::cmdr;

/// Execute a command loop for a scope. This is the main entry point to the cmdr library
/// This function will take commands from the user and try to execute them against the supplied
/// scope until one of the commands returns CommandResult::Quit
pub fn cmd_loop(scope: &mut Scope) -> CommandResult {
    let mut last_result = CommandResult::Ok;

    while last_result == CommandResult::Ok {
        print!("{} ", scope.prompt());
        stdout().flush().unwrap();

        let mut input = String::new();
        stdin().read_line(&mut input).unwrap();

        last_result = scope.command(parse_line(&input));
    }

    last_result
}

/// A parsed line from the user
pub enum Line<'a> {
    /// An empty line
    Empty,

    /// A user command made up of a command and a series of attributes
    Command(&'a str, Vec<&'a str>)
}

/// A command result. returned by one of the client-implemented command methods
#[derive(PartialEq)]
pub enum CommandResult {
    /// Result Ok, ready to go on to the next command
    Ok,

    /// Result Quit, close the application and stop
    Quit
}

fn parse_line<'a>(line: &'a str) -> Line<'a> {
    let mut split_line = line.trim().split(" ");

    match split_line.next() {
        None => Line::Empty,
        Some(command) => Line::Command(command, split_line.collect()),
    }
}

/// Trait for implementing a Scope object. This trait can be implemented by a client but will most
/// likely be implemented for you by the cmdr macro.
pub trait Scope {

    /// Return the prompt for this scope. The default implementation returns > as a prompt but thus
    /// can be overridden to return other strings or implement dynamically generated prompts
    fn prompt(&self) -> String {
        ">".to_string()
    }

    /// Execute a single line
    fn command(&mut self, line: Line) -> CommandResult;

    /// Execute an empty line.
    /// The default implentation does nothing but this can be overridden by a client-application
    /// to implement other behaviour
    fn empty(&mut self) -> CommandResult { CommandResult::Ok }

    /// A user entered an unknown command.
    /// The default implementation prints an error to the user and returns ok to go on. Can be
    /// overridden by a client-application to implement other behaviour
    fn default(&mut self, _line: Line) -> CommandResult {
        println!("Unknown command");
        CommandResult::Ok
    }
}