Giter Club home page Giter Club logo

interact's Introduction

Interact โ€ƒ Build Status Latest Version Docs badge License badge

Interact is a framework for friendly online introspection of the running program state in an intuitive command-line interactive way.

You may be looking for:


Interact is useful for server programs that otherwise receive no input. You can use Interact to make your server receive commands using the special prompt from the interact_prompt crate. The commands can be used to browse your server's internal state, modify it, and call methods that were specified in interact derive attributes.

Interact is implemented for stable Rust, using only safe mode.

Introduction

While dynamically-typed interpreted languages offer the advantage of being able to look at a running program state using a prompt, compiled languages often do not provide that feature. Being hard as it is to introduce interpreters into compiled languages, the Interact project aims to provide a midway solution using stable Rust.

How to make your server Interact-able

  • Custom-derive types using #[derive(Interact)].
    • Use #[interact(skip)] for problematic fields.
    • No need to worry about Rc, RefCell, Arc, or Mutex, even with reference loops. Handling for that exists, as demonstrated later.
  • Register process-global or TLS-local state via interact_prompt's registry.
  • Invoke interact_prompt either directly or in its own OS thread (async not supported yet).

Interact Prompt features

  • Provide Rust-like expressions to explore from the root nodes, e.g. node.some_map["value"].field.sub_field.
  • Full auto-complete and completion hints for type names, field names, enum names, function names, and punctuation.
  • Modify the state from the prompt: at places where mutable access is possible at compile-time, you can assign to fields of inner structs at run-time by appending = <value>.
  • It is possible to call methods that were linked using special interact attributes.
  • State prints have an adjustable limit - if the state is too big it can be automatically capped so your terminal is not overwhelmed.
  • Reference cycles (via Rc or otherwise) are handled gracefully in reflected values - a unique number is printed at all the common sites: the first encounter and the repeats.
  • Data indirection is supported - for example, Actix's Addr<T> can be traversed into, exposing the full server state (see the example in the book).

Interact mini-example with a recorded demo

The program below registers states and invokes the Interact prompt on the main thread.

extern crate interact;

use interact::Interact;
use interact_prompt::{LocalRegistry, Settings};
use std::{cell::RefCell, rc::Rc};

#[derive(Interact)]
struct Point {
    x: i32,
    y: i32,
}

#[derive(Interact)]
struct State {
    maybe_point: Option<Point>,
    complex: ((((usize, usize), u32, (u32, (u32,))), u32), u32),
    behind_rc: Rc<RefCell<u32>>,
    behind_rc2: Rc<RefCell<u32>>,
}

fn main() -> Result<(), interact_prompt::PromptError> {
    let rc = Rc::new(RefCell::new(3));
    let state = State {
        maybe_point: Some(Point { x: 3, y: 3 }),
        complex: ((((0, 0), 0, (0, (0,))), 0), 0),
        behind_rc: rc.clone(),
        behind_rc2: rc,
    };

    LocalRegistry::insert("state", Box::new(state));
    interact_prompt::direct(Settings::default(), ())?;
    Ok(())
}

(this is just one mode for using the Interact prompt. Another mode is running it in the background allowing it to traverse, access, and modify the global process state safely and without interference).

When cloning this repository, it can be run using cargo run --example mini-example. Here's a recorded session:

Getting help

You are more than welcome to browse and open new issues!

License

Interact is licensed under either of

at your option.

Contribution

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

interact's People

Contributors

da-x avatar ivan avatar ninoscript avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

interact's Issues

Derive macro panics if type has doc comment.

Error message:

 #[cfg_attr(feature = "interact", derive(Interact))]
  |                                         ^^^^^^^^
  |
  = help: message: extend () in `interact` data type attribute `Literal { lit: Str_( Memory utilization stats.), suffix: None, span: Span { lo: BytePos(0), hi: BytePos(0), ctxt: #0 } }`

Example panics when typing two periods

Type state.. into the example it will always panic after typing the second period:

# RUST_BACKTRACE=1 ./target/debug/examples/mini-example
Rust `interact`, type '?' for more information
>>> state..

->

e# RUST_BACKTRACE=1 ./target/debug/examples/mini-example
Rust `interact`, type '?' for more information
>>> state.thread 'main' panicked at 'byte index 2 is out of bounds of `.`', src/libcore/str/mod.rs:2196:9
stack backtrace:
   0: backtrace::backtrace::libunwind::trace
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/libunwind.rs:86
   1: backtrace::backtrace::trace_unsynchronized
             at /cargo/registry/src/github.com-1ecc6299db9ec823/backtrace-0.3.44/src/backtrace/mod.rs:66
   2: std::sys_common::backtrace::_print_fmt
             at src/libstd/sys_common/backtrace.rs:78
   3: <std::sys_common::backtrace::_print::DisplayBacktrace as core::fmt::Display>::fmt
             at src/libstd/sys_common/backtrace.rs:59
   4: core::fmt::write
             at src/libcore/fmt/mod.rs:1063
   5: std::io::Write::write_fmt
             at src/libstd/io/mod.rs:1426
   6: std::sys_common::backtrace::_print
             at src/libstd/sys_common/backtrace.rs:62
   7: std::sys_common::backtrace::print
             at src/libstd/sys_common/backtrace.rs:49
   8: std::panicking::default_hook::{{closure}}
             at src/libstd/panicking.rs:204
   9: std::panicking::default_hook
             at src/libstd/panicking.rs:224
  10: std::panicking::rust_panic_with_hook
             at src/libstd/panicking.rs:470
  11: rust_begin_unwind
             at src/libstd/panicking.rs:378
  12: core::panicking::panic_fmt
             at src/libcore/panicking.rs:85
  13: core::str::slice_error_fail
             at src/libcore/str/mod.rs:0
  14: core::str::traits::<impl core::slice::SliceIndex<str> for core::ops::range::RangeFrom<usize>>::index::{{closure}}
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libcore/str/mod.rs:2051
  15: core::option::Option<T>::unwrap_or_else
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libcore/option.rs:428
  16: core::str::traits::<impl core::slice::SliceIndex<str> for core::ops::range::RangeFrom<usize>>::index
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libcore/str/mod.rs:2051
  17: core::str::traits::<impl core::ops::index::Index<I> for str>::index
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libcore/str/mod.rs:1780
  18: <alloc::string::String as core::ops::index::Index<core::ops::range::RangeFrom<usize>>>::index
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/liballoc/string.rs:2017
  19: <interact_prompt::InteractPromptHelper<H> as rustyline::hint::Hinter>::hint
             at ./interact_prompt/src/lib.rs:343
  20: rustyline::edit::State<H>::hint
             at /home/at/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-6.1.2/src/edit.rs:175
  21: rustyline::edit::State<H>::edit_insert
             at /home/at/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-6.1.2/src/edit.rs:310
  22: rustyline::readline_edit
             at /home/at/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-6.1.2/src/lib.rs:468
  23: rustyline::readline_raw
             at /home/at/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-6.1.2/src/lib.rs:697
  24: rustyline::Editor<H>::readline_with
             at /home/at/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-6.1.2/src/lib.rs:826
  25: rustyline::Editor<H>::readline
             at /home/at/.cargo/registry/src/github.com-1ecc6299db9ec823/rustyline-6.1.2/src/lib.rs:802
  26: interact_prompt::direct
             at ./interact_prompt/src/lib.rs:440
  27: mini_example::main
             at interact_prompt/examples/mini-example.rs:31
  28: std::rt::lang_start::{{closure}}
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/rt.rs:67
  29: std::rt::lang_start_internal::{{closure}}
             at src/libstd/rt.rs:52
  30: std::panicking::try::do_call
             at src/libstd/panicking.rs:303
  31: __rust_maybe_catch_panic
             at src/libpanic_unwind/lib.rs:86
  32: std::panicking::try
             at src/libstd/panicking.rs:281
  33: std::panic::catch_unwind
             at src/libstd/panic.rs:394
  34: std::rt::lang_start_internal
             at src/libstd/rt.rs:51
  35: std::rt::lang_start
             at /rustc/8d69840ab92ea7f4d323420088dd8c9775f180cd/src/libstd/rt.rs:67
  36: main
  37: __libc_start_main
  38: _start
note: Some details are omitted, run with `RUST_BACKTRACE=full` for a verbose backtrace.

I see the same thing with the old rustyline, so I don't think I caused this in #15.

Stop interact_prompt::direct from another thread.

I've tried to override and create a custom handler to stop the input thread, but It is not called unless there is a console input (Enter).

It'd be great if the direct version can be stopped from another thread.

Something like this:

struct InteractHandler {
    stop_signal: Weak<()>,
}

impl interact_prompt::Handler for InteractHandler {
    fn receive_interaction(&self, intr: interact_prompt::Interaction) -> interact_prompt::Response {
        log::info!("called only on console input, newline?");
        if self.stop_signal.upgrade().is_none() {
            interact_prompt::Response::Exit
        } else {
            match intr {
                /*interact_prompt::Interaction::Line(string) => {
                    Commands::new().handle_cmd(&string);
                }*/
                _ => {}
            }
            interact_prompt::Response::Continue
        }
    }
}

Client / Server Support

This could be useful in a wide range of applications, if it had the capability to act in a client / server manner.

For example, GTK provides a useful tool for debugging and manipulating GTK widget properties in any GTK application externally with the GTK Inspector. A similar capability could be offered by this and OrbTk.

Varlink could be used as a protocol for communicating messages back and forth between a pipe, UNIX socket, TCP socket, scheme, etc.

Derive macro panics.

My type has this set of attributes

#[derive(Derivative)] // from `derivative` crate.
#[derivative(Debug)]
#[cfg_attr(feature = "interact", derive(Interact)]

Result:

error: proc-macro derive panicked
  --> factory/src/factory.rs:80:41
   |
80 | #[cfg_attr(feature = "interact", derive(Interact))]
   |                                         ^^^^^^^^^^^^^^^^^^
   |
   = help: message: Invalid term Debug in `( Debug )`

Can be reproduced by compiling this

update rustyline

interact_prompt uses a quite outdated rustyline (2.1) It seems as it is not compiling with nightly any more.
I don't know if it is a rustc issue or some experimental feature gated thing, but could you please update it to the latest version (Actually your version is behind to latest by 2 major version)

Improve pagination support

Currently, the reflector in Interact limits the amount of data gathered for each entered expression in interact_prompt. This serves to prevent terminal overflow when trying to print large amount of data, for example if the registered state is very large. However, in order to allow easy probing of very large maps, we would need more flexibility.

  1. The limit for the amount printed data is currently a hardcoded, and we should be able to provide it in the prompt itself.
  2. To more easily navigate through sorted maps (e.g. BTreeMap), it would be nice to provide ranges. For hashmaps, we can provide how much of the iterator's items we would like to skip.
  3. Consider interactive pagination, where Reflection is an ongoing interactive process for which we are able to provide partial results before returning back to the prompt - this will be hard to implement, though.

Panic when typing a large number at the REPL

This happens when typing a large number (I did not press Enter):

# cargo run --example mini-example
    Finished dev [unoptimized + debuginfo] target(s) in 0.05s
     Running `target/debug/examples/mini-example`
Rust `interact`, type '?' for more information
>>> state.complex.0.0.0.0 = 10000000000000000000thread 'main' panicked at 'IntError(ParseIntError { kind: Overflow })', interact/src/root.rs:123:67
note: Run with `RUST_BACKTRACE=1` environment variable to display a backtrace.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.