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