rusteval/lib.rs
1//! This crate provides traits and macros that make your application's structs and functions interactive.
2//!
3//! Annotating a struct with `#[derive(Interactive)]`, a struct's methods with `#[Methods]`
4//! and a free function with `#[Function]` will implement a set of traits,
5//! that will allow you to access them as if Rust had a REPL.
6//!
7//! Use this crate as an alternative for "print debugging" or
8//! as an ergonomic testing API.
9//!
10//! This crate is `no_std` compatible so you can use it to interact with embedded devices
11//! and blink those LEDs from a USB or UART connection.
12//!
13//! # Usage
14//! * Annotate everything you want to access with [`Interactive`], [`Methods`] and [`Function`]
15//! * Define a new struct that owns or holds references to the objects you want to access
16//! * Derive [`InteractiveRoot`] for it
17//! * Use the trait's methods to evaluate a string
18//! (the simplest one is [`eval_to_string`](InteractiveRoot::eval_to_string) but others allow for more custom behaviour)
19//! * Accessing a field will give you its Debug representation
20//! * Calling a function or a method will parse its arguments and give you the Debug representation of its return value
21//!
22//! [`Interactive`]: macro@Interactive
23//! [`Methods`]: macro@Methods
24//! [`Function`]: macro@Function
25//! [`InteractiveRoot`]: macro@InteractiveRoot
26//!
27//! Since this crate makes a lot of use of the [`Debug`] trait the helper macro [`PartialDebug`] is provided.
28//! It implements `Debug` for a struct replacing all fields that do not implement `Debug` with a placeholder.
29//!
30//! [`Debug`]: core::fmt::Debug
31//!
32//! ### CLI Usage
33//! Functions like [`get_all_field_names`](Interactive::get_all_field_names) are provided.
34//! This makes it possible to implement things like auto-completion.
35//!
36//! Have a look at the autocomplete example for how this might be done using the [rustyline](https://docs.rs/crate/rustyline) crate.
37//!
38//! # Example
39//! ```
40//! use rusteval::{Interactive, Methods, InteractiveRoot, Function, PartialDebug};
41//!
42//! #[derive(Default)]
43//! struct NoDebug;
44//!
45//! #[derive(Interactive, PartialDebug, Default)]
46//! struct ChildStruct {
47//! last_sum: f32,
48//! no_debug: NoDebug,
49//! }
50//!
51//! #[Methods]
52//! impl ChildStruct {
53//! fn add(&mut self, a: f32, b: f32) -> f32 {
54//! self.last_sum = a + b;
55//! self.last_sum
56//! }
57//! }
58//!
59//! #[derive(Interactive, Debug, Default)]
60//! struct ParentStruct {
61//! child: ChildStruct,
62//! }
63//!
64//! #[derive(InteractiveRoot, Debug, Default)]
65//! struct Root {
66//! parent: ParentStruct,
67//! }
68//!
69//! #[Function]
70//! fn split_str_at(s: &str, mid: usize) -> (&str, &str) {
71//! s.split_at(mid)
72//! }
73//!
74//! let mut root = Root::default();
75//! assert_eq!(root.eval_to_string("parent.child.add(4.2, 6.9)"), "11.1");
76//! assert_eq!(root.eval_to_string("parent.child"), "ChildStruct { last_sum: 11.1, no_debug: Unknown }");
77//! // split_str_at("foobar", 3) => ("foo", "bar")
78//! assert_eq!(root.eval_to_string("split_str_at(\"foobar\", 3)"), "(\"foo\", \"bar\")");
79//! ```
80//!
81//! # How it works
82//! This crate makes use of the unstable `specialization` feature, so it is only available on nightly.
83//!
84//! Methods like `try_as_interactive` are implemented on all types.
85//! The method normally returns an error but in the specialized case
86//! a trait object (`&dyn Interactive` in this case) is returned.
87//!
88//! The macros then implement getters that look something like this:
89//! ```
90//! # use rusteval::*;
91//! # use rusteval::specialization::*;
92//! # struct Stub {
93//! # field1: (),
94//! # field2: (),
95//! # }
96//! # impl Stub {
97//! fn get_field<'a>(&'a self, field_name: &'a str) -> Result<'_, &dyn Interactive> {
98//! match field_name {
99//! "field1" => self.field1.try_as_interactive(),
100//! "field2" => self.field2.try_as_interactive(),
101//! _ => Err(InteractiveError::FieldNotFound {
102//! type_name: "Struct",
103//! field_name,
104//! }),
105//! }
106//! }
107//! # }
108//! ```
109//!
110//! See the macro's documentation for more details.
111//!
112//! # Current limitations:
113//! * Methods and functions can only be made interactive if their argument types are supported
114//! * Enums are not supported
115
116#![allow(incomplete_features)] // TODO re-enable warning
117#![feature(specialization)]
118#![warn(
119 missing_copy_implementations,
120 missing_debug_implementations,
121 missing_docs,
122 trivial_casts,
123 rust_2018_idioms
124)]
125#![cfg_attr(not(feature = "std"), no_std)]
126
127/// Derive this on a struct to make it an interactive access point for your application.
128///
129/// Same as `#[derive(Interactive)]` but with two additional impls:
130/// * [`trait@InteractiveRoot`] with its default methods
131/// * [`trait@Methods`] as a way to access free functions marked with the attribute [`macro@Function`] (only available with default features on).
132///
133///
134/// ```
135/// use rusteval::{Interactive, InteractiveRoot, Methods, Function};
136///
137/// #[derive(Interactive)]
138/// struct SomethingInteractive;
139///
140/// #[Methods]
141/// impl SomethingInteractive{
142/// fn ping(&self) -> &str{
143/// "pong"
144/// }
145/// }
146///
147/// #[Function]
148/// fn add_one(a: u32) -> u32 {
149/// a + 1
150/// }
151///
152/// let something = SomethingInteractive;
153///
154/// #[derive(InteractiveRoot)]
155/// struct Root {
156/// field: SomethingInteractive,
157/// }
158///
159/// let mut root = Root { field: something };
160/// assert_eq!(root.eval_to_string("field.ping()"), "\"pong\"");
161/// assert_eq!(root.eval_to_string("add_one(42)"), "43");
162/// ```
163pub use rusteval_derive::InteractiveRoot;
164
165/// Gives interactive access to a structs fields.
166///
167/// # What it does:
168/// ```
169/// # use rusteval::Interactive;
170/// #
171/// #[derive(Interactive)]
172/// struct Struct {
173/// field1: u32,
174/// field2: u32,
175/// }
176/// ```
177/// Expands to something like:
178/// ```
179/// # use rusteval::*;
180/// # use rusteval::specialization::*;
181/// # use rusteval::InteractiveError::*;
182/// # use core::fmt::Debug;
183/// #
184/// # struct Struct {
185/// # field1: u32,
186/// # field2: u32,
187/// # }
188/// impl Interactive for Struct {
189/// fn get_field<'a>(&'a self, field_name: &'a str) -> Result<'_, &dyn Interactive> {
190/// match field_name {
191/// "field1" => self.field1.try_as_interactive(),
192/// "field2" => self.field2.try_as_interactive(),
193/// _ => Err(FieldNotFound {
194/// type_name: "Struct",
195/// field_name,
196/// }),
197/// }
198/// }
199/// fn get_field_mut<'a>(&'a mut self, field_name: &'a str) -> Result<'_, &mut dyn Interactive> {
200/// /* ... */
201/// # unimplemented!()
202/// }
203/// fn eval_field(&self, field_name: &str, f: &mut dyn FnMut(Result<'_, &dyn Debug>)) {
204/// match field_name {
205/// "field1" => f(self.field1.try_as_debug()),
206/// /* ... */
207/// # _ => unimplemented!(),
208/// }
209/// }
210/// fn get_all_field_names(&self) -> &'static [&'static str] {
211/// &["field1", "field2"]
212/// }
213/// }
214/// ```
215pub use rusteval_derive::Interactive;
216
217/// Gives interactive access to a structs methods.
218///
219/// Only methods with supported argument types will be made interactive.
220///
221/// Currently supported argument types are:
222///
223/// `bool`, `char`, `f32`, `f64`, `i8`, `i16`, `i32`, `i64`, `i128`, `isize`, `u8`, `u16`, `u32`,
224/// `u64`, `u128`, `usize`, `String`, `str`
225///
226/// References to these types are also supported.
227///
228/// Generic argument types are not supported.
229///
230/// Both `String` and `str` are only available with default features on.
231///
232/// # What it does:
233/// ```
234/// # use rusteval::Methods;
235/// #
236/// # struct Struct;
237/// #
238/// #[Methods]
239/// impl Struct {
240/// fn ping(&self) -> &str {
241/// "pong"
242/// }
243/// fn frob(&mut self, arg: u32){
244/// unimplemented!()
245/// }
246/// }
247/// ```
248/// Expands to something like:
249/// (notice how `frob` is only available inside `eval_method_mut`)
250/// ```
251/// # use core::fmt::Debug;
252/// # use rusteval::*;
253/// # use rusteval::arg_parse::*;
254/// # use rusteval::InteractiveError::*;
255/// #
256/// # struct Struct;
257/// #
258/// # impl Struct {
259/// # fn ping(&self) -> &str {
260/// # "pong"
261/// # }
262/// # fn frob(&mut self, arg: u32){
263/// # unimplemented!()
264/// # }
265/// # }
266/// #
267/// impl Methods for Struct {
268/// fn eval_method(&self, method_name: &str, args: &str, f: &mut dyn FnMut(Result<'_, &dyn Debug>)) {
269/// match method_name {
270/// "ping" => match parse_0_args(method_name, args) {
271/// Ok(()) => f(Ok(&self.ping())),
272/// Err(e) => f(Err(e)),
273/// },
274/// _ => f(Err(MethodNotFound {
275/// type_name: "Struct",
276/// method_name,
277/// })),
278/// }
279/// }
280/// fn eval_method_mut(&mut self, method_name: &str, args: &str, f: &mut dyn FnMut(Result<'_, &dyn Debug>)) {
281/// match method_name {
282/// "ping" => match parse_0_args(method_name, args) {
283/// Ok(()) => f(Ok(&self.ping())),
284/// Err(e) => f(Err(e)),
285/// },
286/// "frob" => match parse_1_arg(method_name, args) {
287/// Ok((arg0,)) => f(Ok(&self.frob(arg0))),
288/// Err(e) => f(Err(e)),
289/// },
290/// _ => f(Err(MethodNotFound {
291/// type_name: "Struct",
292/// method_name,
293/// })),
294/// }
295/// }
296/// fn get_all_method_names(&self) -> &'static [&'static str] {
297/// &["ping", "frob"]
298/// }
299/// }
300/// ```
301pub use rusteval_derive::Methods;
302
303/// Implements [`Debug`] for a struct replacing all fields that do not implement `Debug` with a placeholder.
304///
305/// [`Debug`]: core::fmt::Debug
306///
307/// # What it does:
308/// ```
309/// # use rusteval::PartialDebug;
310///
311/// struct NoDebug;
312///
313/// #[derive(PartialDebug)]
314/// struct Struct {
315/// field: NoDebug,
316/// }
317/// ```
318///
319/// Expands to something like:
320/// ```
321/// # use std::fmt::Debug;
322/// # use core::fmt;
323/// # use rusteval::specialization::AsDebug;
324/// #
325/// # struct NoDebug;
326/// # struct Struct {
327/// # field: NoDebug,
328/// # }
329/// #
330/// impl Debug for Struct {
331/// fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
332/// f.debug_struct("Struct")
333/// .field(
334/// "field",
335/// match &self.field.try_as_debug() {
336/// Ok(field) => field,
337/// Err(_) => &rusteval::specialization::Unknown,
338/// },
339/// )
340/// .finish()
341/// }
342/// }
343///
344/// ```
345pub use rusteval_derive::PartialDebug;
346
347/// Gives interactive access to a function.
348///
349/// Can be used in different modules.
350///
351/// This makes use of the [inventory](https://docs.rs/inventory/*/inventory/) crate
352/// to submit a wrapper struct to a global registry.
353///
354/// You can gain access to the wrapped function by using `#[derive(InteractiveRoot)]`. ([link])
355///
356/// Since the inventory crate requires std this macro is only available with default features on.
357///
358/// [link]: macro@crate::InteractiveRoot
359/// # What it does:
360/// ```
361/// # use rusteval::Function;
362/// #
363/// #[Function]
364/// fn add_one(a: u32) -> u32 {
365/// a + 1
366/// }
367/// ```
368/// Expands to something like:
369/// ```
370/// # use core::fmt::Debug;
371/// # use rusteval::*;
372/// # use rusteval::arg_parse::*;
373/// # use rusteval::inventory;
374///
375/// # fn add_one(a: u32) -> u32 {
376/// # a + 1
377/// # }
378/// #
379/// struct FunctionXYZ;
380/// impl Function for FunctionXYZ {
381/// fn eval(&self, args: &str, f: &mut dyn FnMut(Result<'_, &dyn Debug>)) {
382/// match parse_1_arg("add_one", args) {
383/// Ok((arg0,)) => f(Ok(&add_one(arg0))),
384/// Err(e) => f(Err(e)),
385/// }
386/// }
387/// fn function_name(&self) -> &'static str {
388/// "add_one"
389/// }
390/// }
391/// inventory::submit! {
392/// &FunctionXYZ as &dyn::rusteval::Function
393/// }
394///
395/// ```
396#[cfg(feature = "std")]
397pub use rusteval_derive::Function;
398
399pub use error::{ArgParseError, InteractiveError, Result};
400#[cfg(feature = "std")]
401pub use function::Function;
402pub use interactive::{Interactive, Methods};
403pub use root::InteractiveRoot;
404
405#[cfg(feature = "std")]
406#[doc(hidden)]
407pub use inventory;
408
409pub mod arg_parse;
410mod error;
411mod function;
412mod interactive;
413mod root;
414pub mod specialization;