1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
use std::marker::PhantomData;

use seance::{backend::SessionBackend, Session};

use crate::{
    core::{Handler, HandlerError, HandlerInput, PredicateResult, TryFromInput},
    dialogue::state::DialogueState,
};

/// A predicate for dialogue
///
/// Allows to decide whether a dialogue should start or not.
/// The dialogue handler runs only when his state exists in a session
/// or when the inner predicate returns `true`.
pub struct DialoguePredicate<B, P, PI, HS> {
    session_backend: PhantomData<B>,
    predicate: P,
    predicate_input: PhantomData<PI>,
    handler_state: PhantomData<HS>,
}

impl<B, P, PI, HS> DialoguePredicate<B, P, PI, HS> {
    /// Creates a new `DialoguePredicate`.
    ///
    /// # Arguments
    ///
    /// * `predicate` - The inner predicate (e.g. command).
    pub fn new(predicate: P) -> Self {
        Self {
            session_backend: PhantomData,
            predicate,
            predicate_input: PhantomData,
            handler_state: PhantomData,
        }
    }
}

impl<B, P, PI, HS> Clone for DialoguePredicate<B, P, PI, HS>
where
    P: Clone,
{
    fn clone(&self) -> Self {
        Self {
            session_backend: self.session_backend,
            predicate: self.predicate.clone(),
            predicate_input: self.predicate_input,
            handler_state: self.handler_state,
        }
    }
}

impl<B, P, PI, PO, HS> Handler<HandlerInput> for DialoguePredicate<B, P, PI, HS>
where
    B: SessionBackend + Send + Sync + 'static,
    P: Handler<PI, Output = PO> + Sync + 'static,
    PI: TryFromInput + Sync,
    PI::Error: 'static,
    PO: Into<PredicateResult>,
    HS: DialogueState + Send + Sync,
{
    type Output = PredicateResult;

    async fn handle(&self, input: HandlerInput) -> Self::Output {
        let mut session = match <Session<B>>::try_from_input(input.clone()).await {
            Ok(Some(session)) => session,
            Ok(None) => unreachable!("TryFromInput implementation for Session<B> never returns None"),
            Err(err) => return PredicateResult::Err(HandlerError::new(err)),
        };
        let session_key = HS::session_key();
        match session.get::<&str, HS>(&session_key).await {
            Ok(Some(_)) => {
                // We have dialogue state in session, so we must run dialog handler
                PredicateResult::True
            }
            Ok(None) => {
                // Dialogue state not found in session, let's check predicate
                match PI::try_from_input(input.clone()).await {
                    Ok(Some(predicate_input)) => self.predicate.handle(predicate_input).await.into(),
                    Ok(None) => PredicateResult::False,
                    Err(err) => PredicateResult::Err(HandlerError::new(err)),
                }
            }
            Err(err) => PredicateResult::Err(HandlerError::new(err)),
        }
    }
}