use async_trait::async_trait;
use endbasic_core::ast::{ArgSep, Expr, Value};
use endbasic_core::exec::{self, BuiltinCommand, Machine, MachineBuilder};
use futures_lite::future::block_on;
use std::cell::RefCell;
use std::io;
use std::rc::Rc;
const INPUT: &str = r#"
' Use the custom STORE command to save a string value "out of band".
STORE "some long text"
' Use the custom RETRIEVE command to print the previously-stored value by STORE.
RETRIEVE
"#;
struct StoreCommand {
state: Rc<RefCell<String>>,
}
#[async_trait(?Send)]
impl BuiltinCommand for StoreCommand {
fn name(&self) -> &'static str {
"STORE"
}
fn syntax(&self) -> &'static str {
"\"string to save\""
}
fn description(&self) -> &'static str {
"Saves the string argument given to it in a Rust variable."
}
async fn exec(
&self,
args: &[(Option<Expr>, ArgSep)],
machine: &mut Machine,
) -> exec::Result<()> {
if args.len() != 1 {
return exec::new_usage_error("STORE takes one argument only");
}
let expr = args[0].0.as_ref().expect("A single argument can never be empty");
match expr.eval(machine.get_vars())? {
Value::Text(t) => *self.state.borrow_mut() = t,
_ => return exec::new_usage_error("Mismatched expression type"),
}
Ok(())
}
}
struct RetrieveCommand {
state: Rc<RefCell<String>>,
}
#[async_trait(?Send)]
impl BuiltinCommand for RetrieveCommand {
fn name(&self) -> &'static str {
"RETRIEVE"
}
fn syntax(&self) -> &'static str {
""
}
fn description(&self) -> &'static str {
"Retrieves the string saved by `StoreCommand` and prints it to the console."
}
async fn exec(
&self,
args: &[(Option<Expr>, ArgSep)],
_machine: &mut Machine,
) -> exec::Result<()> {
if !args.is_empty() {
return exec::new_usage_error("RETRIEVE takes no arguments");
}
println!("The stored value was: {}", self.state.borrow());
Ok(())
}
}
fn main() {
let state = Rc::from(RefCell::from(String::new()));
let mut machine = MachineBuilder::default()
.add_builtin(Rc::from(StoreCommand { state: state.clone() }))
.add_builtin(Rc::from(RetrieveCommand { state }))
.build();
let mut cursor = io::Cursor::new(INPUT.as_bytes());
block_on(machine.exec(&mut cursor)).expect("Execution error");
}