Skip to main content

kui_value/
message.rs

1//! Typed messages over the plain-data payload.
2//!
3//! Payloads are [`Value`]s so that Lua, C and JSX share them. A Rust app
4//! gets an exhaustive `match` back with `#[derive(Message)]` (from
5//! `kui-derive`, re-exported by `kui-native` and, behind the `derive`
6//! feature, by this crate and `kui-core`): it turns an enum into a
7//! `{kind, ...fields}` map and back, so `on_click(Msg::Save)` builds the
8//! payload and kui-core's `UiEvent::message::<Msg>()` reads it.
9//!
10//! ```rust,ignore
11//! // Needs the `derive` feature (on by default in kui-native).
12//! #[derive(kui::Message)]
13//! enum Msg {
14//!     Save,
15//!     Rename { to: String },
16//! }
17//!
18//! fn update(ev: &kui::UiEvent) {
19//!     match ev.message::<Msg>() {
20//!         Some(Msg::Save) => { /* ... */ }
21//!         Some(Msg::Rename { to }) => { /* ... */ }
22//!         None => {}
23//!     }
24//! }
25//! ```
26//!
27//! The derive is built from [`MessageField`], the conversion each field's
28//! type has, and [`MessageError`], what a payload that is not one of the
29//! enum's says. Both are plain enough to implement by hand for a type the
30//! derive does not cover. kui-core's `EventField` is what an event's data
31//! type adds to `MessageField`, the field it rides under (ADR 0047,
32//! decision 13).
33
34use crate::Value;
35
36/// A type a message field can hold: to a [`Value`] and back. Implemented
37/// for the numbers, `bool`, `String`, `Value` itself, `Option` (absent or
38/// null is `None`) and `Vec`; `#[derive(Message)]` implements it for the
39/// type it derives, so one message can carry another.
40pub trait MessageField: Sized {
41    fn to_value(self) -> Value;
42    /// `None` for a value of the wrong shape.
43    fn from_value(v: &Value) -> Option<Self>;
44}
45
46/// Why a payload is not the message it was read as.
47#[derive(Clone, Debug, PartialEq, Eq)]
48pub enum MessageError {
49    /// It has no `kind` string (and, for a string-shaped message, is not a
50    /// string).
51    NoKind,
52    /// Its `kind` names no variant of the type.
53    UnknownKind(String),
54    /// A field is missing, or holds a value of the wrong shape.
55    Field { kind: String, field: &'static str },
56}
57
58impl std::fmt::Display for MessageError {
59    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60        match self {
61            MessageError::NoKind => f.write_str("the payload has no `kind`"),
62            MessageError::UnknownKind(k) => write!(f, "no message is of kind {k:?}"),
63            MessageError::Field { kind, field } => write!(
64                f,
65                "a {kind:?} message's `{field}` is missing or of the wrong type"
66            ),
67        }
68    }
69}
70
71impl std::error::Error for MessageError {}
72
73/// Field `key` of the map `v`, as `T`: what the derive reads each field
74/// with. An absent key reads as null, so an `Option` field may be left out.
75pub fn field<T: MessageField>(v: &Value, kind: &str, key: &'static str) -> Result<T, MessageError> {
76    T::from_value(v.get(key).unwrap_or(&Value::Null)).ok_or_else(|| MessageError::Field {
77        kind: kind.to_string(),
78        field: key,
79    })
80}
81
82/// The `kind` of a map payload, for the derive's match.
83pub fn kind_of(v: &Value) -> Result<&str, MessageError> {
84    v.get_str("kind").ok_or(MessageError::NoKind)
85}
86
87impl MessageField for Value {
88    fn to_value(self) -> Value {
89        self
90    }
91    fn from_value(v: &Value) -> Option<Self> {
92        Some(v.clone())
93    }
94}
95
96impl MessageField for bool {
97    fn to_value(self) -> Value {
98        Value::Bool(self)
99    }
100    fn from_value(v: &Value) -> Option<Self> {
101        v.as_bool()
102    }
103}
104
105impl MessageField for String {
106    fn to_value(self) -> Value {
107        Value::Str(self)
108    }
109    fn from_value(v: &Value) -> Option<Self> {
110        v.as_str().map(str::to_string)
111    }
112}
113
114impl MessageField for f64 {
115    fn to_value(self) -> Value {
116        Value::Float(self)
117    }
118    fn from_value(v: &Value) -> Option<Self> {
119        v.as_float()
120    }
121}
122
123impl MessageField for f32 {
124    fn to_value(self) -> Value {
125        Value::float(self)
126    }
127    fn from_value(v: &Value) -> Option<Self> {
128        v.as_float().map(|f| f as f32)
129    }
130}
131
132/// The integers, through `Value::Int`, refusing one out of the type's range
133/// rather than wrapping it.
134macro_rules! int_fields {
135    ($($t:ty),*) => {$(
136        impl MessageField for $t {
137            fn to_value(self) -> Value {
138                Value::Int(i64::try_from(self).unwrap_or(i64::MAX))
139            }
140            fn from_value(v: &Value) -> Option<Self> {
141                v.as_int().and_then(|n| <$t>::try_from(n).ok())
142            }
143        }
144    )*};
145}
146int_fields!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
147
148impl<T: MessageField> MessageField for Option<T> {
149    fn to_value(self) -> Value {
150        self.map_or(Value::Null, T::to_value)
151    }
152    fn from_value(v: &Value) -> Option<Self> {
153        match v {
154            Value::Null => Some(None),
155            v => T::from_value(v).map(Some),
156        }
157    }
158}
159
160impl<T: MessageField> MessageField for Vec<T> {
161    fn to_value(self) -> Value {
162        Value::List(self.into_iter().map(T::to_value).collect())
163    }
164    fn from_value(v: &Value) -> Option<Self> {
165        v.as_list()?.iter().map(T::from_value).collect()
166    }
167}
168
169impl<T: MessageField> MessageField for Box<T> {
170    fn to_value(self) -> Value {
171        (*self).to_value()
172    }
173    fn from_value(v: &Value) -> Option<Self> {
174        T::from_value(v).map(Box::new)
175    }
176}