use async_trait::async_trait;
use endbasic_core::ast::{ArgSep, Expr, Value, VarType};
use endbasic_core::eval::{CallableMetadata, CallableMetadataBuilder};
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 {
metadata: CallableMetadata,
state: Rc<RefCell<String>>,
}
impl StoreCommand {
pub fn new(state: Rc<RefCell<String>>) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("STORE", VarType::Void)
.with_syntax("\"string to save\"")
.with_category("Demonstration")
.with_description("Saves the string argument given to it in a Rust variable.")
.build(),
state,
})
}
}
#[async_trait(?Send)]
impl BuiltinCommand for StoreCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
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(), machine.get_functions())? {
Value::Text(t) => *self.state.borrow_mut() = t,
_ => return exec::new_usage_error("Mismatched expression type"),
}
Ok(())
}
}
struct RetrieveCommand {
metadata: CallableMetadata,
state: Rc<RefCell<String>>,
}
impl RetrieveCommand {
pub fn new(state: Rc<RefCell<String>>) -> Rc<Self> {
Rc::from(Self {
metadata: CallableMetadataBuilder::new("RETRIEVE", VarType::Void)
.with_syntax("")
.with_category("Demonstration")
.with_description(
"Retrieves the string saved by STORE and prints it to the console.",
)
.build(),
state,
})
}
}
#[async_trait(?Send)]
impl BuiltinCommand for RetrieveCommand {
fn metadata(&self) -> &CallableMetadata {
&self.metadata
}
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_command(StoreCommand::new(state.clone()))
.add_command(RetrieveCommand::new(state))
.build();
let mut cursor = io::Cursor::new(INPUT.as_bytes());
block_on(machine.exec(&mut cursor)).expect("Execution error");
}