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