Skip to main content

elfo_core/
actor_status.rs

1use std::{
2    fmt, mem,
3    sync::atomic::{self, AtomicU8},
4};
5
6use serde::{Deserialize, Serialize};
7
8// === ActorStatus ===
9
10/// Represents the current status of an actor.
11/// [The Actoromicon](https://actoromicon.rs/ch02-01-actor-lifecycle.html).
12#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
13pub struct ActorStatus {
14    pub(crate) kind: ActorStatusKind,
15    pub(crate) details: Option<String>,
16}
17
18impl ActorStatus {
19    const fn new(kind: ActorStatusKind) -> Self {
20        Self {
21            kind,
22            details: None,
23        }
24    }
25
26    /// Creates a new status with the same kind and provided details.
27    pub fn with_details(&self, details: impl fmt::Display) -> Self {
28        ActorStatus {
29            kind: self.kind,
30            details: Some(details.to_string()),
31        }
32    }
33
34    /// Returns the corresponding [`ActorStatusKind`] for this status.
35    pub fn kind(&self) -> ActorStatusKind {
36        self.kind
37    }
38
39    /// Returns details for this status, if provided.
40    pub fn details(&self) -> Option<&str> {
41        self.details.as_deref()
42    }
43}
44
45#[cfg(not(feature = "test-util"))]
46impl ActorStatus {
47    pub const ALARMING: ActorStatus = ActorStatus::new(ActorStatusKind::Alarming);
48    pub(crate) const FAILED: ActorStatus = ActorStatus::new(ActorStatusKind::Failed);
49    pub const INITIALIZING: ActorStatus = ActorStatus::new(ActorStatusKind::Initializing);
50    pub const NORMAL: ActorStatus = ActorStatus::new(ActorStatusKind::Normal);
51    pub(crate) const TERMINATED: ActorStatus = ActorStatus::new(ActorStatusKind::Terminated);
52    pub const TERMINATING: ActorStatus = ActorStatus::new(ActorStatusKind::Terminating);
53}
54
55#[cfg(feature = "test-util")]
56impl ActorStatus {
57    pub const ALARMING: ActorStatus = ActorStatus::new(ActorStatusKind::Alarming);
58    pub const FAILED: ActorStatus = ActorStatus::new(ActorStatusKind::Failed);
59    pub const INITIALIZING: ActorStatus = ActorStatus::new(ActorStatusKind::Initializing);
60    pub const NORMAL: ActorStatus = ActorStatus::new(ActorStatusKind::Normal);
61    pub const TERMINATED: ActorStatus = ActorStatus::new(ActorStatusKind::Terminated);
62    pub const TERMINATING: ActorStatus = ActorStatus::new(ActorStatusKind::Terminating);
63}
64
65impl fmt::Display for ActorStatus {
66    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
67        match &self.details {
68            Some(details) => write!(f, "{:?}: {}", self.kind, details),
69            None => write!(f, "{:?}", self.kind),
70        }
71    }
72}
73
74// === ActorStatusKind ===
75
76/// A list specifying statuses of actors. It's used with the [`ActorStatus`].
77#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
78#[non_exhaustive]
79#[repr(u8)]
80pub enum ActorStatusKind {
81    Normal,
82    Initializing,
83    Terminating,
84    Terminated,
85    Alarming,
86    Failed,
87}
88
89impl ActorStatusKind {
90    #[inline]
91    pub const fn is_normal(&self) -> bool {
92        matches!(self, Self::Normal)
93    }
94
95    #[inline]
96    pub const fn is_initializing(&self) -> bool {
97        matches!(self, Self::Initializing)
98    }
99
100    #[inline]
101    pub const fn is_terminating(&self) -> bool {
102        matches!(self, Self::Terminating)
103    }
104
105    #[inline]
106    pub const fn is_terminated(&self) -> bool {
107        matches!(self, Self::Terminated)
108    }
109
110    #[inline]
111    pub const fn is_alarming(&self) -> bool {
112        matches!(self, Self::Alarming)
113    }
114
115    #[inline]
116    pub const fn is_failed(&self) -> bool {
117        matches!(self, Self::Failed)
118    }
119
120    #[inline]
121    pub const fn is_finished(&self) -> bool {
122        self.is_failed() || self.is_terminated()
123    }
124}
125
126impl ActorStatusKind {
127    pub(crate) fn as_str(&self) -> &'static str {
128        match self {
129            ActorStatusKind::Normal => "Normal",
130            ActorStatusKind::Initializing => "Initializing",
131            ActorStatusKind::Terminating => "Terminating",
132            ActorStatusKind::Terminated => "Terminated",
133            ActorStatusKind::Alarming => "Alarming",
134            ActorStatusKind::Failed => "Failed",
135        }
136    }
137}
138
139// === AtomicActorStatusKind ===
140
141#[derive(Debug)]
142#[repr(transparent)]
143pub(crate) struct AtomicActorStatusKind(AtomicU8);
144
145impl From<ActorStatusKind> for AtomicActorStatusKind {
146    fn from(value: ActorStatusKind) -> Self {
147        Self(AtomicU8::new(value as _))
148    }
149}
150
151impl AtomicActorStatusKind {
152    pub(crate) fn store(&self, kind: ActorStatusKind, ordering: atomic::Ordering) {
153        self.0.store(kind as u8, ordering);
154    }
155
156    pub(crate) fn load(&self, ordering: atomic::Ordering) -> ActorStatusKind {
157        let result = self.0.load(ordering);
158
159        // SAFETY: `ActorStatusKind` has `#[repr(u8)]` annotation. The only
160        // place where value may be changed is `Self::store`, which consumes
161        // `ActorStatusKind`, thus, guarantees that possibly invalid value
162        // cannot be stored
163        unsafe { mem::transmute::<u8, ActorStatusKind>(result) }
164    }
165}