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
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
//! Module with the [`Spawn`] trait.

use heph::actor::{self, NewActor};
use heph::actor_ref::ActorRef;
use heph::supervisor::Supervisor;

pub mod options;

pub(crate) use private::{AddActorError, PrivateSpawn};

#[doc(no_inline)]
pub use options::{ActorOptions, FutureOptions, SyncActorOptions};

/// The `Spawn` trait defines how new actors are added to the runtime.
pub trait Spawn<S, NA, RT>: PrivateSpawn<S, NA, RT> {
    /// Attempts to spawn an actor.
    ///
    /// Arguments:
    /// * `supervisor`: all actors need supervision, the `supervisor` is the
    ///   supervisor for this actor, see the [`Supervisor`] trait for more
    ///   information.
    /// * `new_actor`: the [`NewActor`] implementation that defines how to start
    ///   the actor.
    /// * `arg`: the argument(s) passed when starting the actor, and
    /// * `options`: the actor options used to spawn the new actors.
    ///
    /// When using a [`NewActor`] implementation that never returns an error,
    /// such as the implementation provided by async functions, it's easier to
    /// use the [`spawn`] method.
    ///
    /// [`spawn`]: Spawn::spawn
    fn try_spawn(
        &mut self,
        supervisor: S,
        new_actor: NA,
        arg: NA::Argument,
        options: ActorOptions,
    ) -> Result<ActorRef<NA::Message>, NA::Error>
    where
        S: Supervisor<NA>,
        NA: NewActor<RuntimeAccess = RT>,
    {
        self.try_spawn_setup(supervisor, new_actor, |_| Ok(arg), options)
            .map_err(|err| match err {
                AddActorError::NewActor(err) => err,
                AddActorError::<_, !>::ArgFn(_) => unreachable!(),
            })
    }

    /// Spawn an actor.
    ///
    /// This is a convenience method for `NewActor` implementations that never
    /// return an error, such as asynchronous functions.
    ///
    /// See [`Spawn::try_spawn`] for more information.
    fn spawn(
        &mut self,
        supervisor: S,
        new_actor: NA,
        arg: NA::Argument,
        options: ActorOptions,
    ) -> ActorRef<NA::Message>
    where
        S: Supervisor<NA>,
        NA: NewActor<Error = !, RuntimeAccess = RT>,
    {
        self.try_spawn_setup(supervisor, new_actor, |_| Ok(arg), options)
            .unwrap_or_else(|_: AddActorError<!, !>| unreachable!())
    }
}

mod private {
    //! Module with private types.

    use heph::actor::{self, NewActor};
    use heph::actor_ref::ActorRef;
    use heph::supervisor::Supervisor;

    use crate::spawn::ActorOptions;

    /// Private version of the [`Spawn`]  trait.
    ///
    /// [`Spawn`]: super::Spawn
    pub trait PrivateSpawn<S, NA, RT> {
        /// Spawn an actor that needs to be initialised.
        ///
        /// See the public [`Spawn`] trait for documentation on the arguments.
        ///
        /// [`Spawn`]: super::Spawn
        #[allow(clippy::type_complexity)] // Not part of the public API, so it's OK.
        fn try_spawn_setup<ArgFn, E>(
            &mut self,
            supervisor: S,
            new_actor: NA,
            arg_fn: ArgFn,
            options: ActorOptions,
        ) -> Result<ActorRef<NA::Message>, AddActorError<NA::Error, E>>
        where
            S: Supervisor<NA>,
            NA: NewActor<RuntimeAccess = RT>,
            ArgFn: FnOnce(&mut actor::Context<NA::Message, RT>) -> Result<NA::Argument, E>;
    }

    /// Error returned by spawning a actor.
    #[derive(Debug)]
    pub enum AddActorError<NewActorE, ArgFnE> {
        /// Calling `NewActor::new` actor resulted in an error.
        NewActor(NewActorE),
        /// Calling the argument function resulted in an error.
        ArgFn(ArgFnE),
    }
}

impl<M, RT, S, NA, RT2> Spawn<S, NA, RT2> for actor::Context<M, RT> where RT: Spawn<S, NA, RT2> {}

impl<M, RT, S, NA, RT2> PrivateSpawn<S, NA, RT2> for actor::Context<M, RT>
where
    RT: PrivateSpawn<S, NA, RT2>,
{
    fn try_spawn_setup<ArgFn, E>(
        &mut self,
        supervisor: S,
        new_actor: NA,
        arg_fn: ArgFn,
        options: ActorOptions,
    ) -> Result<ActorRef<NA::Message>, AddActorError<NA::Error, E>>
    where
        S: Supervisor<NA>,
        NA: NewActor<RuntimeAccess = RT2>,
        ArgFn: FnOnce(&mut actor::Context<NA::Message, RT2>) -> Result<NA::Argument, E>,
    {
        self.runtime()
            .try_spawn_setup(supervisor, new_actor, arg_fn, options)
    }
}