use std::fmt;
use std::sync::Arc;
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub struct Message {
pub sender: u64,
pub reply_cap: u64,
pub request_id: u64,
pub tag: u16,
pub payload: u64,
}
impl Message {
pub const fn new(sender: u64, request_id: u64, tag: u16, payload: u64) -> Self {
Self {
sender,
reply_cap: 0,
request_id,
tag,
payload,
}
}
#[inline]
pub(crate) fn authenticate(mut self, sender: u64, reply_cap: u64) -> Self {
self.sender = sender;
self.reply_cap = reply_cap;
self
}
}
impl fmt::Display for Message {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(
f,
"msg{{from=flow#{}, reply=cap#{}, id={}, tag={}, payload={}}}",
self.sender, self.reply_cap, self.request_id, self.tag, self.payload
)
}
}
#[derive(Clone, Debug, PartialEq)]
pub enum Value {
Unit,
Bool(bool),
Int(i64),
Float(f64),
Pid(u64),
Message(Message),
Cap(u64),
Str(Arc<str>),
Bytes(Arc<[u8]>),
}
impl Value {
#[inline]
pub fn str(s: impl AsRef<str>) -> Self {
Value::Str(Arc::from(s.as_ref()))
}
#[inline]
pub fn bytes(b: impl AsRef<[u8]>) -> Self {
Value::Bytes(Arc::from(b.as_ref()))
}
#[inline]
pub fn is_truthy(&self) -> bool {
match self {
Value::Unit | Value::Bool(false) | Value::Int(0) => false,
Value::Str(s) if s.is_empty() => false,
Value::Bytes(b) if b.is_empty() => false,
_ => true,
}
}
#[inline]
pub fn as_int(&self) -> Option<i64> {
match self {
Value::Int(i) => Some(*i),
Value::Bool(b) => Some(*b as i64),
_ => None,
}
}
#[inline]
pub fn as_pid(&self) -> Option<u64> {
match self {
Value::Pid(p) => Some(*p),
_ => None,
}
}
#[inline]
pub fn as_cap(&self) -> Option<u64> {
match self {
Value::Cap(c) => Some(*c),
_ => None,
}
}
#[inline]
pub fn as_message(&self) -> Option<Message> {
match self {
Value::Message(m) => Some(*m),
_ => None,
}
}
#[inline]
pub fn as_str(&self) -> Option<&str> {
match self {
Value::Str(s) => Some(s.as_ref()),
_ => None,
}
}
#[inline]
pub fn as_bytes(&self) -> Option<&[u8]> {
match self {
Value::Bytes(b) => Some(b.as_ref()),
Value::Str(s) => Some(s.as_bytes()),
_ => None,
}
}
#[inline]
pub fn memory_size(&self) -> usize {
std::mem::size_of::<Self>() + self.heap_size()
}
#[inline]
pub fn heap_size(&self) -> usize {
match self {
Value::Str(s) => s.len(),
Value::Bytes(b) => b.len(),
Value::Unit
| Value::Bool(_)
| Value::Int(_)
| Value::Float(_)
| Value::Pid(_)
| Value::Message(_)
| Value::Cap(_) => 0,
}
}
pub fn type_name(&self) -> &'static str {
match self {
Value::Unit => "unit",
Value::Bool(_) => "bool",
Value::Int(_) => "int",
Value::Float(_) => "float",
Value::Pid(_) => "pid",
Value::Message(_) => "message",
Value::Cap(_) => "cap",
Value::Str(_) => "str",
Value::Bytes(_) => "bytes",
}
}
}
impl fmt::Display for Value {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
Value::Unit => write!(f, "()"),
Value::Bool(b) => write!(f, "{b}"),
Value::Int(i) => write!(f, "{i}"),
Value::Float(x) => write!(f, "{x}"),
Value::Pid(p) => write!(f, "flow#{p}"),
Value::Message(m) => write!(f, "{m}"),
Value::Cap(c) => write!(f, "cap#{c}"),
Value::Str(s) => write!(f, "{s}"),
Value::Bytes(b) => write!(f, "bytes[{}]", b.len()),
}
}
}
impl From<i64> for Value {
fn from(v: i64) -> Self {
Value::Int(v)
}
}
impl From<bool> for Value {
fn from(v: bool) -> Self {
Value::Bool(v)
}
}
impl From<f64> for Value {
fn from(v: f64) -> Self {
Value::Float(v)
}
}
impl From<Message> for Value {
fn from(m: Message) -> Self {
Value::Message(m)
}
}
impl From<&str> for Value {
fn from(s: &str) -> Self {
Value::str(s)
}
}
impl From<String> for Value {
fn from(s: String) -> Self {
Value::Str(Arc::from(s))
}
}
impl From<&[u8]> for Value {
fn from(b: &[u8]) -> Self {
Value::bytes(b)
}
}
impl From<Vec<u8>> for Value {
fn from(b: Vec<u8>) -> Self {
Value::Bytes(Arc::from(b))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn message_is_truthy_and_round_trips_helpers() {
let m = Message::new(7, 99, 10, 1);
let v = Value::Message(m);
assert!(v.is_truthy());
assert_eq!(v.as_message(), Some(m));
assert_eq!(v.type_name(), "message");
}
#[test]
fn authenticate_stamps_sender_and_reply_cap() {
let m = Message::new(999, 1, 2, 3).authenticate(42, 7);
assert_eq!(m.sender, 42);
assert_eq!(m.reply_cap, 7);
assert_eq!(m.request_id, 1);
}
#[test]
fn cap_is_truthy() {
assert!(Value::Cap(1).is_truthy());
assert_eq!(Value::Cap(3).as_cap(), Some(3));
}
#[test]
fn str_and_bytes_helpers() {
let s = Value::str("hi");
assert_eq!(s.as_str(), Some("hi"));
assert_eq!(s.type_name(), "str");
assert!(s.is_truthy());
assert!(!Value::str("").is_truthy());
let b = Value::bytes([1u8, 2, 3]);
assert_eq!(b.as_bytes(), Some(&[1, 2, 3][..]));
assert_eq!(b.type_name(), "bytes");
assert!(b.is_truthy());
assert!(!Value::bytes([]).is_truthy());
assert_eq!(s.as_bytes(), Some(b"hi".as_slice()));
}
#[test]
fn str_eq_compares_content() {
assert_eq!(Value::str("a"), Value::from("a".to_owned()));
assert_ne!(Value::str("a"), Value::str("b"));
assert_eq!(Value::bytes([9]), Value::from(vec![9u8]));
}
}