intrepid-core 0.1.6

Manage complex async business logic with ease
Documentation
mod candidate_action;

use crate::Handler;

use super::{Action, Active, Ready, Stateless};

pub use candidate_action::CandidateAction;

/// The `Candidate` type state represents an action that has not yet been given a state.
/// It holds on to phantom markers so that type resolution can properly trace the types
/// it leverages.
///
#[derive(Clone)]
pub struct Candidate<ActionHandler: Clone, Args: Clone, State: Clone> {
    action: ActionHandler,
    _args: std::marker::PhantomData<Args>,
    _state: std::marker::PhantomData<State>,
}

impl<ActionHandler: Clone, Args: Clone, State: Clone> Candidate<ActionHandler, Args, State> {
    /// Create a new candidate from an action.
    pub fn new(action: ActionHandler) -> Self {
        Self {
            action,
            _args: std::marker::PhantomData,
            _state: std::marker::PhantomData,
        }
    }
}

impl<ActionHandler, Args, State> From<Candidate<ActionHandler, Args, State>>
    for Action<Candidate<ActionHandler, Args, State>>
where
    ActionHandler: Handler<Args, State> + Clone + Send + 'static,
    Args: Clone + Send + 'static,
    State: Clone + Send + 'static,
{
    fn from(candidate: Candidate<ActionHandler, Args, State>) -> Self {
        Self {
            action_state: candidate,
        }
    }
}

#[tokio::test]
async fn creating_and_resuming_statelessly() -> Result<(), tower::BoxError> {
    use tower::Service;

    let action = || async {};
    let action = action.candidate();

    assert_eq!(action.stateless().call(()).await?, ().into());

    Ok(())
}

#[tokio::test]
async fn creating_and_resuming_with_state() -> Result<(), tower::BoxError> {
    use tower::Service;

    use crate::State;

    #[derive(Clone)]
    struct ArbitraryState;

    impl From<ArbitraryState> for crate::Frame {
        fn from(_: ArbitraryState) -> Self {
            "ArbitraryState".to_owned().into()
        }
    }

    let action = |State(state): State<ArbitraryState>| async { state };
    let action = action.candidate();

    assert_eq!(
        action.with_state(ArbitraryState).call(()).await?,
        "ArbitraryState".to_string().into()
    );

    Ok(())
}