use core::any::{TypeId, type_name};
use core::ops::{Deref, DerefMut};
use crate::Environment;
use alloc::{collections::BTreeMap, format};
use anyhow::Error;
pub trait Extractor: 'static + Sized {
fn extract(env: &Environment) -> Result<Self, Error>;
fn extract_from_action(env: &Environment, state: &mut ExtractionState) -> Result<Self, Error> {
let _ = state;
Self::extract(env)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Use<T: 'static>(pub T);
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct State<T: 'static>(pub T);
#[derive(Debug, Clone, Default)]
pub struct ExtractionState {
positions: BTreeMap<TypeId, usize>,
}
impl ExtractionState {
#[must_use]
pub fn take_next<T: 'static>(&mut self) -> usize {
let position = self.positions.entry(TypeId::of::<T>()).or_insert(0);
let current = *position;
*position += 1;
current
}
}
impl Extractor for Environment {
fn extract(env: &Environment) -> Result<Self, Error> {
Ok(env.clone())
}
}
impl<T> Deref for Use<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for Use<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T> Deref for State<T> {
type Target = T;
fn deref(&self) -> &Self::Target {
&self.0
}
}
impl<T> DerefMut for State<T> {
fn deref_mut(&mut self) -> &mut Self::Target {
&mut self.0
}
}
impl<T: Extractor> Extractor for Option<T> {
fn extract(env: &Environment) -> Result<Self, Error> {
Ok(<T as Extractor>::extract(env).ok())
}
fn extract_from_action(env: &Environment, state: &mut ExtractionState) -> Result<Self, Error> {
let snapshot = state.clone();
T::extract_from_action(env, state).map_or_else(
|_| {
*state = snapshot;
Ok(None)
},
|value| Ok(Some(value)),
)
}
}
impl<T: 'static + Clone> Extractor for Use<T> {
fn extract(env: &Environment) -> Result<Self, Error> {
env.get::<T>().map_or_else(
|| {
Err(Error::msg(format!(
"Environment value `{}` not found",
type_name::<T>()
)))
},
|value| Ok(Self(value.clone())),
)
}
}
impl<T: 'static + Clone> Extractor for State<T> {
fn extract(env: &Environment) -> Result<Self, Error> {
env.get::<Self>().map_or_else(
|| {
Err(Error::msg(format!(
"Environment state `{}` not found",
type_name::<T>()
)))
},
|value| Ok(value.clone()),
)
}
fn extract_from_action(env: &Environment, state: &mut ExtractionState) -> Result<Self, Error> {
let position = state.take_next::<Self>();
env.get_nth::<Self>(position).map_or_else(
|| {
Err(Error::msg(format!(
"Environment state `{}` not found at position {}",
type_name::<T>(),
position
)))
},
|value| Ok(value.clone()),
)
}
}
macro_rules! impl_tuple_extractor {
($($T:ident),+) => {
impl<$($T: Extractor),+> Extractor for ($($T,)+) {
fn extract(env: &Environment) -> Result<Self, Error> {
Ok(($($T::extract(env)?,)+))
}
fn extract_from_action(
env: &Environment,
state: &mut ExtractionState,
) -> Result<Self, Error> {
Ok(($($T::extract_from_action(env, state)?,)+))
}
}
};
}
impl_tuple_extractor!(A, B);
impl_tuple_extractor!(A, B, C);
impl_tuple_extractor!(A, B, C, D);
impl_tuple_extractor!(A, B, C, D, E);
impl_tuple_extractor!(A, B, C, D, E, F);
impl_tuple_extractor!(A, B, C, D, E, F, G);
impl_tuple_extractor!(A, B, C, D, E, F, G, H);