[−][src]Crate repl_rs
repl-rs - REPL library for Rust
Example
use std::collections::HashMap; use repl_rs::{Command, Error, Parameter, Result, Value}; use repl_rs::{Convert, Repl}; // Write "Hello" fn hello<T>(args: HashMap<String, Value>, _context: &mut T) -> Result<Option<String>> { Ok(Some(format!("Hello, {}", args["who"]))) } fn main() -> Result<()> { let mut repl = Repl::new(()) .with_name("MyApp") .with_version("v0.1.0") .with_description("My very cool app") .add_command( Command::new("hello", hello) .with_parameter(Parameter::new("who").set_required(true)?)? .with_help("Greetings!"), ); repl.run() }
repl-rs uses the builder pattern extensively. What these lines are doing is:
- creating a repl with an empty Context (see below)
- with a name of "MyApp", the given version, and the given description
- and adding a "hello" command which calls out to the
hello
callback function defined above - the
hello
command has a single parameter, "who", which is required, and has the given help message
The hello
function takes a HashMap of named arguments, contained in a
Value struct, and an (unused) Context
, which is used to hold state if you
need to - the initial context is passed in to the call to
Repl::new, in our case, ()
.
Because we're not using a Context, we need to include a generic type in our hello
function,
because there's no way to pass an argument of type ()
otherwise.
All command function callbacks return a Result<Option<String>>
. This has the following
effect:
- If the return is
Ok(Some(String))
, it prints the string to stdout - If the return is
Ok(None)
, it prints nothing - If the return is an error, it prints the error message to stderr
Conversions
The Value type has conversions defined for all the primitive types. Here's how that works in practice:
use repl_rs::{Command, Parameter, Result, Value}; use repl_rs::{Convert, Repl}; use std::collections::HashMap; // Add two numbers. fn add<T>(args: HashMap<String, Value>, _context: &mut T) -> Result<Option<String>> { let first: i32 = args["first"].convert()?; let second: i32 = args["second"].convert()?; Ok(Some((first + second).to_string())) } fn main() -> Result<()> { let mut repl = Repl::new(()) .with_name("MyApp") .with_version("v0.1.0") .with_description("My very cool app") .add_command( Command::new("add", add) .with_parameter(Parameter::new("first").set_required(true)?)? .with_parameter(Parameter::new("second").set_required(true)?)? .with_help("Add two numbers together"), ); repl.run() }
This example adds two numbers. The convert()
function manages the conversion for you.
Context
The Context
type is used to keep state between REPL calls. Here's an example:
use repl_rs::{Command, Parameter, Result, Value}; use repl_rs::{Convert, Repl}; use std::collections::{HashMap, VecDeque}; #[derive(Default)] struct Context { list: VecDeque<String>, } // Append name to list fn append(args: HashMap<String, Value>, context: &mut Context) -> Result<Option<String>> { let name: String = args["name"].convert()?; context.list.push_back(name); let list: Vec<String> = context.list.clone().into(); Ok(Some(list.join(", "))) } // Prepend name to list fn prepend(args: HashMap<String, Value>, context: &mut Context) -> Result<Option<String>> { let name: String = args["name"].convert()?; context.list.push_front(name); let list: Vec<String> = context.list.clone().into(); Ok(Some(list.join(", "))) } fn main() -> Result<()> { let mut repl = Repl::new(Context::default()) .add_command( Command::new("append", append) .with_parameter(Parameter::new("name").set_required(true)?)? .with_help("Append name to end of list"), ) .add_command( Command::new("prepend", prepend) .with_parameter(Parameter::new("name").set_required(true)?)? .with_help("Prepend name to front of list"), ); repl.run() }
A few things to note:
- you pass in the initial value for your Context struct to the call to Repl::new()
- the context is passed to your command callback functions as a mutable reference
Help
repl-rs has support for supplying help commands for your REPL. This is accomplished through the HelpViewer, which is a trait that has a default implementation which should give you pretty much what you expect.
% myapp
Welcome to MyApp v0.1.0
MyApp> help
MyApp v0.1.0: My very cool app
------------------------------
append - Append name to end of list
prepend - Prepend name to front of list
MyApp> help append
append: Append name to end of list
Usage:
append name
MyApp>
If you want to roll your own help, just implement HelpViewer and add it to your REPL using the .with_help_viewer() method.
Structs
Command | Struct to define a command in the REPL |
HelpContext | Struct which gets sent to HelpViewer when |
HelpEntry | Help entry which gets sent to HelpViewer when help for a particular command is requested |
Parameter | Command parameter |
Repl | Main REPL struct |
Value | Value type. Has conversions to every primitive type. |
Enums
Error | Error type |
Traits
Convert | Trait to convert from a Value to some other type. |
HelpViewer | Trait to be used if you want your own custom Help output |
Type Definitions
Callback | Command callback function signature |
Result | Result type |