use alloc::string::String;
use alloc::vec::Vec;
use crate::datamodel::{Datamodel, Environment, EvalError, Event, SessionInfo, StateQuery, Value};
use crate::session::{Input, StartInput};
use crate::trace::MacrostepTrace;
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Request {
InitSession(SessionInfo),
SetEnvironment(Environment),
Declare {
id: String,
},
SetEvent(Event),
EvalCond {
expr: String,
},
EvalValue {
expr: String,
},
EvalContent {
content: String,
},
AssignExpr {
location: String,
expr: String,
},
AssignValue {
location: String,
value: Value,
},
BindVar {
name: String,
value: Value,
},
IsLocation {
location: String,
},
ExecScript {
script: String,
},
ForeachItems {
array: String,
},
Restore(Value),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Answer {
Unit,
Bool(bool),
Value(Value),
Values(Vec<Value>),
Error(EvalError),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct TapeEntry {
pub request: Request,
pub answer: Answer,
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum OracleInput {
Start(StartInput),
Step(Input),
}
#[derive(Clone, Debug, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct OracleRecord {
pub input: OracleInput,
pub tape: Vec<TapeEntry>,
pub trace: MacrostepTrace,
}
pub struct TapeDatamodel<'a> {
inner: &'a mut dyn Datamodel,
entries: Vec<TapeEntry>,
}
impl<'a> TapeDatamodel<'a> {
pub fn new(inner: &'a mut dyn Datamodel) -> Self {
TapeDatamodel {
inner,
entries: Vec::new(),
}
}
pub fn entries(&self) -> &[TapeEntry] {
&self.entries
}
pub fn take(&mut self) -> Vec<TapeEntry> {
core::mem::take(&mut self.entries)
}
fn log(&mut self, request: Request, answer: Answer) {
self.entries.push(TapeEntry { request, answer });
}
}
fn unit(r: &Result<(), EvalError>) -> Answer {
match r {
Ok(()) => Answer::Unit,
Err(e) => Answer::Error(e.clone()),
}
}
impl Datamodel for TapeDatamodel<'_> {
fn name(&self) -> &str {
self.inner.name()
}
fn init_session(&mut self, info: &SessionInfo) -> Result<(), EvalError> {
let r = self.inner.init_session(info);
self.log(Request::InitSession(info.clone()), unit(&r));
r
}
fn set_environment(&mut self, env: &Environment) {
self.inner.set_environment(env);
self.log(Request::SetEnvironment(*env), Answer::Unit);
}
fn declare(&mut self, id: &str) -> Result<(), EvalError> {
let r = self.inner.declare(id);
self.log(Request::Declare { id: id.into() }, unit(&r));
r
}
fn set_event(&mut self, event: &Event) {
self.inner.set_event(event);
self.log(Request::SetEvent(event.clone()), Answer::Unit);
}
fn check_event(&self, event: &Event) -> Result<(), EvalError> {
self.inner.check_event(event)
}
fn eval_cond(&mut self, expr: &str, states: &dyn StateQuery) -> Result<bool, EvalError> {
let r = self.inner.eval_cond(expr, states);
let a = match &r {
Ok(b) => Answer::Bool(*b),
Err(e) => Answer::Error(e.clone()),
};
self.log(Request::EvalCond { expr: expr.into() }, a);
r
}
fn eval_value(&mut self, expr: &str, states: &dyn StateQuery) -> Result<Value, EvalError> {
let r = self.inner.eval_value(expr, states);
let a = match &r {
Ok(v) => Answer::Value(v.clone()),
Err(e) => Answer::Error(e.clone()),
};
self.log(Request::EvalValue { expr: expr.into() }, a);
r
}
fn eval_content(&mut self, content: &str) -> Result<Value, EvalError> {
let r = self.inner.eval_content(content);
let a = match &r {
Ok(v) => Answer::Value(v.clone()),
Err(e) => Answer::Error(e.clone()),
};
self.log(
Request::EvalContent {
content: content.into(),
},
a,
);
r
}
fn assign_expr(
&mut self,
location: &str,
expr: &str,
states: &dyn StateQuery,
) -> Result<(), EvalError> {
let r = self.inner.assign_expr(location, expr, states);
self.log(
Request::AssignExpr {
location: location.into(),
expr: expr.into(),
},
unit(&r),
);
r
}
fn assign_value(&mut self, location: &str, value: Value) -> Result<(), EvalError> {
let req = Request::AssignValue {
location: location.into(),
value: value.clone(),
};
let r = self.inner.assign_value(location, value);
self.log(req, unit(&r));
r
}
fn bind_var(&mut self, name: &str, value: Value) -> Result<(), EvalError> {
let req = Request::BindVar {
name: name.into(),
value: value.clone(),
};
let r = self.inner.bind_var(name, value);
self.log(req, unit(&r));
r
}
fn is_location(&mut self, location: &str) -> bool {
let r = self.inner.is_location(location);
self.log(
Request::IsLocation {
location: location.into(),
},
Answer::Bool(r),
);
r
}
fn exec_script(&mut self, script: &str, states: &dyn StateQuery) -> Result<(), EvalError> {
let r = self.inner.exec_script(script, states);
self.log(
Request::ExecScript {
script: script.into(),
},
unit(&r),
);
r
}
fn foreach_items(
&mut self,
array: &str,
states: &dyn StateQuery,
) -> Result<Vec<Value>, EvalError> {
let r = self.inner.foreach_items(array, states);
let a = match &r {
Ok(v) => Answer::Values(v.clone()),
Err(e) => Answer::Error(e.clone()),
};
self.log(
Request::ForeachItems {
array: array.into(),
},
a,
);
r
}
fn snapshot(&self) -> Value {
self.inner.snapshot()
}
fn restore(&mut self, snapshot: &Value) -> Result<(), EvalError> {
let r = self.inner.restore(snapshot);
self.log(Request::Restore(snapshot.clone()), unit(&r));
r
}
}