1use std::fmt;
2use std::sync::Arc;
3
4use super::cap::CapId;
5
6pub const TAG_SYS_DOWN: u16 = 0xFF01;
8pub const TAG_SYS_EXIT: u16 = 0xFF02;
10
11#[derive(Clone, Debug, PartialEq)]
26pub struct Message {
27 pub sender: u64,
29 pub reply_cap: CapId,
31 pub request_id: u64,
33 pub tag: u16,
35 pub payload: Arc<Value>,
37}
38
39impl Message {
40 pub fn new(sender: u64, request_id: u64, tag: u16, payload: impl Into<Value>) -> Self {
43 Self {
44 sender,
45 reply_cap: CapId::NONE,
46 request_id,
47 tag,
48 payload: Arc::new(payload.into()),
49 }
50 }
51
52 pub fn request(request_id: u64, tag: u16, payload: impl Into<Value>) -> Self {
54 Self::new(0, request_id, tag, payload)
55 }
56
57 pub fn reply_to(req: &Self, tag: u16, payload: impl Into<Value>) -> Self {
59 Self::new(0, req.request_id, tag, payload)
60 }
61
62 pub fn down(monitor: u64, target_flow: u64, reason: u64) -> Self {
64 Self::new(
65 target_flow,
66 monitor,
67 TAG_SYS_DOWN,
68 Value::Int(reason as i64),
69 )
70 }
71
72 pub fn linked_exit(target_flow: u64, reason: u64) -> Self {
74 Self::new(target_flow, 0, TAG_SYS_EXIT, Value::Int(reason as i64))
75 }
76
77 #[inline]
78 pub fn is_down(&self) -> bool {
79 self.tag == TAG_SYS_DOWN
80 }
81
82 #[inline]
83 pub fn is_exit(&self) -> bool {
84 self.tag == TAG_SYS_EXIT
85 }
86
87 #[inline]
89 pub(crate) fn authenticate(mut self, sender: u64, reply_cap: CapId) -> Self {
90 self.sender = sender;
91 self.reply_cap = reply_cap;
92 self
93 }
94}
95
96impl fmt::Display for Message {
97 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
98 write!(
99 f,
100 "msg{{from=flow#{}, reply={}, id={}, tag={}, payload={}}}",
101 self.sender, self.reply_cap, self.request_id, self.tag, self.payload
102 )
103 }
104}
105
106#[derive(Clone, Debug, PartialEq)]
114pub enum Value {
115 Unit,
116 Bool(bool),
117 Int(i64),
118 Float(f64),
119 Pid(u64),
121 Message(Message),
123 Cap(CapId),
125 Str(Arc<str>),
127 Bytes(Arc<[u8]>),
129}
130
131impl Value {
132 #[inline]
134 pub fn str(s: impl AsRef<str>) -> Self {
135 Value::Str(Arc::from(s.as_ref()))
136 }
137
138 #[inline]
140 pub fn bytes(b: impl AsRef<[u8]>) -> Self {
141 Value::Bytes(Arc::from(b.as_ref()))
142 }
143
144 #[inline]
147 pub fn is_truthy(&self) -> bool {
148 match self {
149 Value::Unit | Value::Bool(false) | Value::Int(0) => false,
150 Value::Str(s) if s.is_empty() => false,
151 Value::Bytes(b) if b.is_empty() => false,
152 _ => true,
153 }
154 }
155
156 #[inline]
157 pub fn as_int(&self) -> Option<i64> {
158 match self {
159 Value::Int(i) => Some(*i),
160 Value::Bool(b) => Some(*b as i64),
161 _ => None,
162 }
163 }
164
165 #[inline]
166 pub fn as_pid(&self) -> Option<u64> {
167 match self {
168 Value::Pid(p) => Some(*p),
169 _ => None,
170 }
171 }
172
173 #[inline]
174 pub fn as_cap(&self) -> Option<CapId> {
175 match self {
176 Value::Cap(c) => Some(*c),
177 _ => None,
178 }
179 }
180
181 #[inline]
182 pub fn as_message(&self) -> Option<&Message> {
183 match self {
184 Value::Message(m) => Some(m),
185 _ => None,
186 }
187 }
188
189 #[inline]
190 pub fn as_str(&self) -> Option<&str> {
191 match self {
192 Value::Str(s) => Some(s.as_ref()),
193 _ => None,
194 }
195 }
196
197 #[inline]
198 pub fn as_bytes(&self) -> Option<&[u8]> {
199 match self {
200 Value::Bytes(b) => Some(b.as_ref()),
201 Value::Str(s) => Some(s.as_bytes()),
202 _ => None,
203 }
204 }
205
206 #[inline]
213 pub fn memory_size(&self) -> usize {
214 std::mem::size_of::<Self>() + self.heap_size()
215 }
216
217 #[inline]
220 pub fn heap_size(&self) -> usize {
221 match self {
222 Value::Str(s) => s.len(),
223 Value::Bytes(b) => b.len(),
224 Value::Message(m) => m.payload.memory_size(),
225 Value::Unit
226 | Value::Bool(_)
227 | Value::Int(_)
228 | Value::Float(_)
229 | Value::Pid(_)
230 | Value::Cap(_) => 0,
231 }
232 }
233
234 pub fn type_name(&self) -> &'static str {
235 match self {
236 Value::Unit => "unit",
237 Value::Bool(_) => "bool",
238 Value::Int(_) => "int",
239 Value::Float(_) => "float",
240 Value::Pid(_) => "pid",
241 Value::Message(_) => "message",
242 Value::Cap(_) => "cap",
243 Value::Str(_) => "str",
244 Value::Bytes(_) => "bytes",
245 }
246 }
247}
248
249impl fmt::Display for Value {
250 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
251 match self {
252 Value::Unit => write!(f, "()"),
253 Value::Bool(b) => write!(f, "{b}"),
254 Value::Int(i) => write!(f, "{i}"),
255 Value::Float(x) => write!(f, "{x}"),
256 Value::Pid(p) => write!(f, "flow#{p}"),
257 Value::Message(m) => write!(f, "{m}"),
258 Value::Cap(c) => write!(f, "{c}"),
259 Value::Str(s) => write!(f, "{s}"),
260 Value::Bytes(b) => write!(f, "bytes[{}]", b.len()),
261 }
262 }
263}
264
265impl From<i64> for Value {
266 fn from(v: i64) -> Self {
267 Value::Int(v)
268 }
269}
270impl From<i32> for Value {
271 fn from(v: i32) -> Self {
272 Value::Int(i64::from(v))
273 }
274}
275impl From<u64> for Value {
276 fn from(v: u64) -> Self {
277 Value::Int(v as i64)
278 }
279}
280impl From<bool> for Value {
281 fn from(v: bool) -> Self {
282 Value::Bool(v)
283 }
284}
285impl From<f64> for Value {
286 fn from(v: f64) -> Self {
287 Value::Float(v)
288 }
289}
290impl From<Message> for Value {
291 fn from(m: Message) -> Self {
292 Value::Message(m)
293 }
294}
295impl From<CapId> for Value {
296 fn from(c: CapId) -> Self {
297 Value::Cap(c)
298 }
299}
300impl From<&str> for Value {
301 fn from(s: &str) -> Self {
302 Value::str(s)
303 }
304}
305impl From<String> for Value {
306 fn from(s: String) -> Self {
307 Value::Str(Arc::from(s))
308 }
309}
310impl From<&[u8]> for Value {
311 fn from(b: &[u8]) -> Self {
312 Value::bytes(b)
313 }
314}
315impl From<Vec<u8>> for Value {
316 fn from(b: Vec<u8>) -> Self {
317 Value::Bytes(Arc::from(b))
318 }
319}
320
321#[cfg(test)]
322mod tests {
323 use super::*;
324
325 #[test]
326 fn message_is_truthy_and_round_trips_helpers() {
327 let m = Message::new(7, 99, 10, 1u64);
328 let v = Value::Message(m.clone());
329 assert!(v.is_truthy());
330 assert_eq!(v.as_message(), Some(&m));
331 assert_eq!(v.type_name(), "message");
332 assert_eq!(m.payload.as_ref(), &Value::Int(1));
333 }
334
335 #[test]
336 fn authenticate_stamps_sender_and_reply_cap() {
337 let reply = CapId::from_raw(7);
338 let m = Message::new(999, 1, 2, 3u64).authenticate(42, reply);
339 assert_eq!(m.sender, 42);
340 assert_eq!(m.reply_cap, reply);
341 assert_eq!(m.request_id, 1);
342 }
343
344 #[test]
345 fn str_payload_is_charged_on_the_hop() {
346 let hop = Value::Message(Message::new(1, 1, 1, Value::str("hello")));
347 assert!(hop.heap_size() >= 5);
348 }
349
350 #[test]
351 fn cap_is_truthy() {
352 let cap = CapId::from_raw(3);
353 assert!(Value::Cap(cap).is_truthy());
354 assert_eq!(Value::Cap(cap).as_cap(), Some(cap));
355 }
356
357 #[test]
358 fn str_and_bytes_helpers() {
359 let s = Value::str("hi");
360 assert_eq!(s.as_str(), Some("hi"));
361 assert_eq!(s.type_name(), "str");
362 assert!(s.is_truthy());
363 assert!(!Value::str("").is_truthy());
364
365 let b = Value::bytes([1u8, 2, 3]);
366 assert_eq!(b.as_bytes(), Some(&[1, 2, 3][..]));
367 assert_eq!(b.type_name(), "bytes");
368 assert!(b.is_truthy());
369 assert!(!Value::bytes([]).is_truthy());
370
371 assert_eq!(s.as_bytes(), Some(b"hi".as_slice()));
372 }
373
374 #[test]
375 fn str_eq_compares_content() {
376 assert_eq!(Value::str("a"), Value::from("a".to_owned()));
377 assert_ne!(Value::str("a"), Value::str("b"));
378 assert_eq!(Value::bytes([9]), Value::from(vec![9u8]));
379 }
380}