use core::fmt::Debug;
use crate::{Interactive, InteractiveError, Result};
enum AccessType<'a> {
FieldAccess(&'a str),
MethodAccess(&'a str, &'a str),
}
pub trait InteractiveRoot: Interactive + Sized {
#[cfg(feature = "std")]
fn eval_to_string(&mut self, query: &str) -> String {
let mut s = String::new();
self.try_eval_mut(query, |result| {
s = match result {
Ok(r) => format!("{:?}", r),
Err(e) => format!("{}", e),
}
});
s
}
fn eval_and_write<T>(&mut self, query: &str, buf: &mut T) -> core::fmt::Result
where
T: core::fmt::Write,
{
let mut r = Ok(());
self.try_eval_mut(query, |result| {
r = match result {
Ok(r) => write!(buf, "{:?}", r),
Err(e) => write!(buf, "{}", e),
}
});
r
}
fn try_eval<F>(&self, query: &str, mut f: F)
where
F: FnMut(Result<'_, &dyn Debug>),
{
match self.get_queried_object(query) {
Ok((object, rest_expression)) => {
let access_type = parse_access_type(rest_expression);
match access_type {
Ok(AccessType::FieldAccess(field_name)) => {
object.eval_field(field_name, &mut f)
}
Ok(AccessType::MethodAccess(method_name, args)) => {
match object.try_as_methods() {
Ok(obj) => obj.eval_method(method_name, args, &mut f),
Err(e) => f(Err(e)),
}
}
Err(e) => f(Err(e)),
}
}
Err(e) => f(Err(e)),
}
}
fn try_eval_mut<F>(&mut self, query: &str, mut f: F)
where
F: FnMut(Result<'_, &dyn Debug>),
{
match self.get_queried_object_mut(query) {
Ok((object, rest_expression)) => {
let access_type = parse_access_type(rest_expression);
match access_type {
Ok(AccessType::FieldAccess(field_name)) => {
object.eval_field(field_name, &mut f)
}
Ok(AccessType::MethodAccess(method_name, args)) => {
match object.try_as_methods_mut() {
Ok(obj) => obj.eval_method_mut(method_name, args, &mut f),
Err(e) => f(Err(e)),
}
}
Err(e) => f(Err(e)),
}
}
Err(InteractiveError::FieldNotFound { .. }) => self.try_eval(query, f), Err(e) => f(Err(e)),
}
}
fn get_queried_object<'a>(&'a self, query: &'a str) -> Result<'_, (&dyn Interactive, &str)> {
let (mut object_path, rest_expression) = parse_object_path(query);
let mut current: &dyn Interactive = self;
while !object_path.is_empty() {
let (field_name, object_path_remainder) = object_path
.split_once('.')
.unwrap_or((object_path.trim(), ""));
object_path = object_path_remainder;
current = current.get_field(field_name.trim())?
}
Ok((current, rest_expression))
}
fn get_queried_object_mut<'a>(
&'a mut self,
query: &'a str,
) -> Result<'_, (&mut dyn Interactive, &str)> {
let (mut object_path, rest_expression) = parse_object_path(query);
let mut current: &mut dyn Interactive = self;
while !object_path.is_empty() {
let (field_name, object_path_remainder) = object_path
.split_once('.')
.unwrap_or((object_path.trim(), ""));
object_path = object_path_remainder;
current = current.get_field_mut(field_name.trim())?
}
Ok((current, rest_expression))
}
}
fn parse_access_type(expression: &str) -> Result<'_, AccessType<'_>> {
let expression = expression.trim();
match expression.strip_suffix(')').map(|s| s.split_once('(')) {
Some(Some((method_name, args))) => Ok(AccessType::MethodAccess(method_name.trim(), args)),
Some(None) => Err(InteractiveError::SyntaxError), None => Ok(AccessType::FieldAccess(expression)),
}
}
fn parse_object_path(query: &str) -> (&str, &str) {
let args_start_index = query.find('(').unwrap_or(query.len());
match query[..args_start_index].rfind('.') {
Some(last_dot_index) => {
let (object_path, rest_expression) = query.split_at(last_dot_index);
(object_path.trim(), rest_expression.get(1..).unwrap_or(""))
}
None => ("", query),
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_parse_object_path0() {
assert_eq!(parse_object_path(""), ("", ""));
}
#[test]
fn test_parse_object_path1() {
assert_eq!(parse_object_path("foo"), ("", "foo"));
}
#[test]
fn test_parse_object_path2() {
assert_eq!(parse_object_path("foo."), ("foo", ""));
}
#[test]
fn test_parse_object_path3() {
assert_eq!(parse_object_path("foo.bar"), ("foo", "bar"));
}
#[test]
fn test_parse_object_path4() {
assert_eq!(parse_object_path("foo.frob(1.5)"), ("foo", "frob(1.5)"));
}
}