use std::fmt::Debug;
use std::marker::PhantomData;
use crate::ctx::wrap_handler;
use crate::ctx::wrap_handler_action;
use crate::ctx::wrap_handler_fallback;
use crate::ctx::wrap_handler_fallback_action;
use crate::ctx::Ctx;
use crate::ctx::Store;
use crate::map::ErasedTy;
use crate::opt::Opt;
use crate::set::Set;
use crate::set::SetExt;
use crate::set::SetOpt;
use crate::trace;
use crate::Error;
use crate::HashMap;
use crate::Uid;
pub struct Invoker<'a, S> {
callbacks: HashMap<Uid, InvokeHandler<'a, S, Error>>,
}
impl<S> Debug for Invoker<'_, S> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.debug_struct("Invoker")
.field("callbacks", &"{ ... }")
.finish()
}
}
impl<S> Default for Invoker<'_, S> {
fn default() -> Self {
Self {
callbacks: HashMap::default(),
}
}
}
impl<S> Invoker<'_, S> {
pub fn new() -> Self {
Self {
callbacks: HashMap::default(),
}
}
}
impl<'a, S: Set> Invoker<'a, S> {
pub fn set_raw<H: FnMut(&mut S, &mut Ctx) -> Result<bool, Error> + Send + Sync + 'a>(
&mut self,
uid: Uid,
handler: H,
) -> &mut Self {
self.callbacks.insert(uid, Box::new(handler));
self
}
pub fn set_handler<O, H, T>(&mut self, uid: Uid, handler: H, store: T) -> &mut Self
where
O: ErasedTy,
T: Store<S, O, Ret = bool, Error = Error> + Send + Sync + 'a,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
self.set_raw(uid, wrap_handler(handler, store));
self
}
pub fn has(&self, uid: Uid) -> bool {
self.callbacks.contains_key(&uid)
}
}
impl<'a, S> Invoker<'a, S>
where
SetOpt<S>: Opt,
S: Set,
{
pub fn entry<O, H>(&mut self, uid: Uid) -> HandlerEntry<'a, '_, Self, S, H, O>
where
O: ErasedTy,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
HandlerEntry::new(self, uid)
}
pub fn fallback(set: &mut S, ctx: &mut Ctx) -> Result<bool, Error> {
let uid = ctx.uid()?;
let opt = set.get_mut(uid).unwrap();
let arg = ctx.arg()?.map(|v| v.as_ref());
let act = *opt.action();
trace!("in fallback, call for {}({act}) {{{ctx:?}}}", opt.name());
opt.accessor_mut().store_all(arg, ctx, &act)
}
}
pub type InvokeHandler<'a, S, Error> =
Box<dyn FnMut(&mut S, &mut Ctx) -> Result<bool, Error> + Send + Sync + 'a>;
pub trait HandlerCollection<'a, S: Set> {
fn register_handler<H: FnMut(&mut S, &mut Ctx) -> Result<bool, Error> + Send + Sync + 'a>(
&mut self,
uid: Uid,
handler: H,
);
fn get_handler(&mut self, uid: &Uid) -> Option<&mut InvokeHandler<'a, S, Error>>;
fn invoke(&mut self, uid: &Uid, set: &mut S, ctx: &mut Ctx) -> Result<bool, Error> {
trace!("invoking callback of {} {:?}", uid, ctx);
if let Some(callback) = self.get_handler(uid) {
return (callback)(set, ctx);
}
unreachable!(
"no callback of {}, call `invoke_fb` or `fallback` instead",
set.opt(*uid)?.name()
)
}
fn invoke_fb(&mut self, uid: &Uid, set: &mut S, ctx: &mut Ctx) -> Result<bool, Error> {
if let Some(callback) = self.get_handler(uid) {
trace!("invoking(fb) callback of {} {:?}", uid, ctx);
(callback)(set, ctx)
} else {
trace!("invoking(fb) fallback callback of {} {:?}", uid, ctx);
Invoker::fallback(set, ctx)
}
}
}
impl<'a, S: Set> HandlerCollection<'a, S> for Invoker<'a, S> {
fn register_handler<H: FnMut(&mut S, &mut Ctx) -> Result<bool, Error> + Send + Sync + 'a>(
&mut self,
uid: Uid,
handler: H,
) {
self.set_raw(uid, handler);
}
fn get_handler(&mut self, uid: &Uid) -> Option<&mut InvokeHandler<'a, S, Error>> {
self.callbacks.get_mut(uid)
}
}
pub struct HandlerEntry<'a, 'b, I, S, H, O>
where
O: ErasedTy,
S: Set,
SetOpt<S>: Opt,
I: HandlerCollection<'a, S>,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
invoker: &'b mut I,
uid: Uid,
marker: PhantomData<(&'a (), O, S, H)>,
}
impl<'a, 'b, I, S, H, O> HandlerEntry<'a, 'b, I, S, H, O>
where
O: ErasedTy,
S: Set,
SetOpt<S>: Opt,
I: HandlerCollection<'a, S>,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
pub fn new(invoker: &'b mut I, uid: Uid) -> Self {
Self {
invoker,
uid,
marker: PhantomData,
}
}
pub fn on(self, handler: H) -> HandlerEntryThen<'a, 'b, I, S, H, O> {
HandlerEntryThen::new(self.invoker, self.uid, handler, false)
}
pub fn fallback(self, handler: H) -> HandlerEntryThen<'a, 'b, I, S, H, O> {
HandlerEntryThen::new(self.invoker, self.uid, handler, true)
}
}
pub struct HandlerEntryThen<'a, 'b, I, S, H, O>
where
O: ErasedTy,
S: Set,
SetOpt<S>: Opt,
I: HandlerCollection<'a, S>,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
invoker: &'b mut I,
handler: Option<H>,
register: bool,
uid: Uid,
fallback: bool,
marker: PhantomData<(&'a (), O, S)>,
}
impl<'a, 'b, I, S, H, O> HandlerEntryThen<'a, 'b, I, S, H, O>
where
O: ErasedTy,
S: Set,
SetOpt<S>: Opt,
I: HandlerCollection<'a, S>,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
pub fn new(invoker: &'b mut I, uid: Uid, handler: H, fallback: bool) -> Self {
Self {
invoker,
handler: Some(handler),
register: false,
uid,
fallback,
marker: PhantomData,
}
}
pub fn then(
mut self,
store: impl Store<S, O, Ret = bool, Error = Error> + Send + Sync + 'a,
) -> Self {
if !self.register {
if let Some(handler) = self.handler.take() {
if self.fallback {
self.invoker
.register_handler(self.uid, wrap_handler_fallback(handler, store));
} else {
self.invoker
.register_handler(self.uid, wrap_handler(handler, store));
}
}
self.register = true;
}
self
}
pub fn submit(mut self) -> Uid {
if !self.register {
if let Some(handler) = self.handler.take() {
if self.fallback {
self.invoker
.register_handler(self.uid, wrap_handler_fallback_action(handler));
} else {
self.invoker
.register_handler(self.uid, wrap_handler_action(handler));
}
}
self.register = true;
}
self.uid
}
}
impl<'a, I, S, H, O> Drop for HandlerEntryThen<'a, '_, I, S, H, O>
where
O: ErasedTy,
S: Set,
SetOpt<S>: Opt,
I: HandlerCollection<'a, S>,
H: FnMut(&mut S, &mut Ctx) -> Result<Option<O>, Error> + Send + Sync + 'a,
{
fn drop(&mut self) {
if !self.register {
if let Some(handler) = self.handler.take() {
if self.fallback {
self.invoker
.register_handler(self.uid, wrap_handler_fallback_action(handler));
} else {
self.invoker
.register_handler(self.uid, wrap_handler_action(handler));
}
}
self.register = true;
}
}
}