Skip to content

phsym/shrust

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

59 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

License Build Status Coverage Status Crates.io

shrust

Rust library to create interactive command line shells

Documentation

Copyright © 2019 Pierre-Henri Symoneaux

THIS SOFTWARE IS DISTRIBUTED WITHOUT ANY WARRANTY
Check LICENSE.txt file for more information.

This is currently a work in progress, and the API should be consider unstable. I'll start documenting and releasing to crates.io once a first level of stability has been reached

How to use

Including

More often, you will include the library as a dependency to your project. In order to do this, add the following lines to your Cargo.toml file :

[dependencies]
shrust = "0.0.7"

Basic usage

Let's have a look at example dummy.rs :

extern crate shrust;
use shrust::{Shell, ShellIO};
use std::io::prelude::*;

fn main() {
    let mut shell = Shell::new(());
    shell.new_command_noargs("hello", "Say 'hello' to the world", |io, _| {
        writeln!(io, "Hello World !!!")?;
        Ok(())
    });

    shell.run_loop(&mut ShellIO::default());
}

The output of this program would be

λ cargo run --example dummy
     Running `target\debug\examples\dummy.exe`
>help
 hello    :  Say 'hello' to the world
 help     :  Print this help
 history  :  Print commands history or run a command from it
 quit     :  Quit
>hello
Hello World !!!
>quit

Attaching data

You can attach data to the shell for usage by commands as seen in data.rs:

let v = Vec::new();
let mut shell = Shell::new(v);
shell.new_command("push", "Add string to the list", 1, |io, v, s| {
    writeln!(io, "Pushing {}", s[0])?;
    v.push(s[0].to_string());
    Ok(())
});
shell.new_command_noargs("list", "List strings", |io, v| {
    for s in v {
        writeln!(io, "{}", s)?;
    }
    Ok(())
});

shell.run_loop(&mut ShellIO::default());

Output:

λ cargo run --example dummy
     Running `target\debug\examples\dummy.exe`
>help
 help     :  Print this help
 history  :  Print commands history or run a command from it
 list     :  List strings
 push     :  Add string to the list
 quit     :  Quit
>push foo
Pushing foo
>push bar
Pushing bar
>list
foo
bar
>quit

Using custom I/O

In previous examples, the shell's loop was run the following way:

shell.run_loop(&mut ShellIO::default());

ShellIO::default() returns an stdin/stdout IO.

It's possible to create a ShellIO instance around user-defined I/O. For example to connect a Shell on a socket, the ShellIO would be created with

let mut io = ShellIO::new_io(sock);

where sock is the socket, then the shell can be started with

shell.run_loop(&mut io);

This is applied in example socket.rs.

Default handler

By default, when a command is not found, the evaluation returns an UnknownCommand error. This behavior can be customized by providing a custom default handler to be invoked on not found command.

let mut shell = Shell::new(());
shell.set_default(|io, _, cmd| {
    writeln!(io, "Hello from default handler !!! Received: {}", cmd)?;
    Ok(())
});
shell.run_loop(&mut ShellIO::default());

Output:

λ cargo run --example default
     Running `target\debug\examples\default.exe`
>foo
Hello from default handler !!! Received: foo
>quit

This is applied in example default.rs.

Multithreading

A shell instance itself cannot be shared across threads, it needs to be cloned. A shell is clonable only if the wrapped data is clonable too. However, the wrapped data can be easily shared if (for example) it's an Arc around a Sync+Send value.

TBD...

Additional examples are provided in documentation and in examples directory