1use crate::value::Value;
16
17pub trait MessageField: Sized {
22 fn to_value(self) -> Value;
23 fn from_value(v: &Value) -> Option<Self>;
25}
26
27#[derive(Clone, Debug, PartialEq, Eq)]
29pub enum MessageError {
30 NoKind,
33 UnknownKind(String),
35 Field { kind: String, field: &'static str },
37}
38
39impl std::fmt::Display for MessageError {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 MessageError::NoKind => f.write_str("the payload has no `kind`"),
43 MessageError::UnknownKind(k) => write!(f, "no message is of kind {k:?}"),
44 MessageError::Field { kind, field } => write!(
45 f,
46 "a {kind:?} message's `{field}` is missing or of the wrong type"
47 ),
48 }
49 }
50}
51
52impl std::error::Error for MessageError {}
53
54pub fn field<T: MessageField>(v: &Value, kind: &str, key: &'static str) -> Result<T, MessageError> {
57 T::from_value(v.get(key).unwrap_or(&Value::Null)).ok_or_else(|| MessageError::Field {
58 kind: kind.to_string(),
59 field: key,
60 })
61}
62
63pub fn kind_of(v: &Value) -> Result<&str, MessageError> {
65 v.get_str("kind").ok_or(MessageError::NoKind)
66}
67
68impl MessageField for Value {
69 fn to_value(self) -> Value {
70 self
71 }
72 fn from_value(v: &Value) -> Option<Self> {
73 Some(v.clone())
74 }
75}
76
77impl MessageField for bool {
78 fn to_value(self) -> Value {
79 Value::Bool(self)
80 }
81 fn from_value(v: &Value) -> Option<Self> {
82 v.as_bool()
83 }
84}
85
86impl MessageField for String {
87 fn to_value(self) -> Value {
88 Value::Str(self)
89 }
90 fn from_value(v: &Value) -> Option<Self> {
91 v.as_str().map(str::to_string)
92 }
93}
94
95impl MessageField for f64 {
96 fn to_value(self) -> Value {
97 Value::Float(self)
98 }
99 fn from_value(v: &Value) -> Option<Self> {
100 v.as_float()
101 }
102}
103
104impl MessageField for f32 {
105 fn to_value(self) -> Value {
106 Value::float(self)
107 }
108 fn from_value(v: &Value) -> Option<Self> {
109 v.as_float().map(|f| f as f32)
110 }
111}
112
113macro_rules! int_fields {
116 ($($t:ty),*) => {$(
117 impl MessageField for $t {
118 fn to_value(self) -> Value {
119 Value::Int(i64::try_from(self).unwrap_or(i64::MAX))
120 }
121 fn from_value(v: &Value) -> Option<Self> {
122 v.as_int().and_then(|n| <$t>::try_from(n).ok())
123 }
124 }
125 )*};
126}
127int_fields!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
128
129impl<T: MessageField> MessageField for Option<T> {
130 fn to_value(self) -> Value {
131 self.map_or(Value::Null, T::to_value)
132 }
133 fn from_value(v: &Value) -> Option<Self> {
134 match v {
135 Value::Null => Some(None),
136 v => T::from_value(v).map(Some),
137 }
138 }
139}
140
141impl<T: MessageField> MessageField for Vec<T> {
142 fn to_value(self) -> Value {
143 Value::List(self.into_iter().map(T::to_value).collect())
144 }
145 fn from_value(v: &Value) -> Option<Self> {
146 v.as_list()?.iter().map(T::from_value).collect()
147 }
148}
149
150impl<T: MessageField> MessageField for Box<T> {
151 fn to_value(self) -> Value {
152 (*self).to_value()
153 }
154 fn from_value(v: &Value) -> Option<Self> {
155 T::from_value(v).map(Box::new)
156 }
157}
158
159impl crate::input::UiEvent {
160 pub fn message<M>(&self) -> Option<M>
170 where
171 M: for<'a> TryFrom<&'a Value>,
172 {
173 M::try_from(&self.payload)
174 .ok()
175 .or_else(|| self.payload.get("tag").and_then(|t| M::try_from(t).ok()))
176 }
177}