use thiserror::Error;
use crate::dialogue::Value;
#[derive(Error, Debug)]
pub enum HostError {
#[error("unknown function `{0}`")]
UnknownFunction(String),
#[error("`{name}`: `{message}`")]
Failed { name: String, message: String },
#[error("builtin `{name}`: `{message}`")]
BuiltinFailed { name: String, message: String },
}
pub trait RepliqueHost {
fn call(&mut self, name: &str, args: Vec<Value>) -> Result<Value, HostError>;
}
pub struct NoHost;
impl RepliqueHost for NoHost {
fn call(&mut self, name: &str, _: Vec<Value>) -> Result<Value, HostError> {
Err(HostError::UnknownFunction(name.into()))
}
}
#[cfg(test)]
pub(crate) mod test {
use super::*;
#[derive(Default)]
pub(crate) struct TestHost {
pub calls: Vec<(String, Vec<Value>)>,
}
impl TestHost {
pub fn called(&self) -> Vec<&str> {
self.calls.iter().map(|(name, _)| name.as_str()).collect()
}
}
impl RepliqueHost for TestHost {
fn call(&mut self, name: &str, args: Vec<Value>) -> Result<Value, HostError> {
self.calls.push((name.to_owned(), args.clone()));
let failed = |message: &str| HostError::Failed {
name: name.to_owned(),
message: message.to_owned(),
};
match name {
"upper_new" => match args.as_slice() {
[Value::String(text)] => Ok(Value::String(text.to_uppercase())),
_ => Err(failed("expected one string")),
},
"double" => match args.as_slice() {
[Value::Int(n)] => Ok(Value::Int(n * 2)),
_ => Err(failed("expected one int")),
},
"is_even" => match args.as_slice() {
[Value::Int(n)] => Ok(Value::Bool(n % 2 == 0)),
_ => Err(failed("expected one int")),
},
"boom" => Err(failed("this function always fails")),
_ => Err(HostError::UnknownFunction(name.to_owned())),
}
}
}
}