use crate::value::Value;
pub trait MessageField: Sized {
fn to_value(self) -> Value;
fn from_value(v: &Value) -> Option<Self>;
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum MessageError {
NoKind,
UnknownKind(String),
Field { kind: String, field: &'static str },
}
impl std::fmt::Display for MessageError {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
MessageError::NoKind => f.write_str("the payload has no `kind`"),
MessageError::UnknownKind(k) => write!(f, "no message is of kind {k:?}"),
MessageError::Field { kind, field } => write!(
f,
"a {kind:?} message's `{field}` is missing or of the wrong type"
),
}
}
}
impl std::error::Error for MessageError {}
pub fn field<T: MessageField>(v: &Value, kind: &str, key: &'static str) -> Result<T, MessageError> {
T::from_value(v.get(key).unwrap_or(&Value::Null)).ok_or_else(|| MessageError::Field {
kind: kind.to_string(),
field: key,
})
}
pub fn kind_of(v: &Value) -> Result<&str, MessageError> {
v.get_str("kind").ok_or(MessageError::NoKind)
}
impl MessageField for Value {
fn to_value(self) -> Value {
self
}
fn from_value(v: &Value) -> Option<Self> {
Some(v.clone())
}
}
impl MessageField for bool {
fn to_value(self) -> Value {
Value::Bool(self)
}
fn from_value(v: &Value) -> Option<Self> {
v.as_bool()
}
}
impl MessageField for String {
fn to_value(self) -> Value {
Value::Str(self)
}
fn from_value(v: &Value) -> Option<Self> {
v.as_str().map(str::to_string)
}
}
impl MessageField for f64 {
fn to_value(self) -> Value {
Value::Float(self)
}
fn from_value(v: &Value) -> Option<Self> {
v.as_float()
}
}
impl MessageField for f32 {
fn to_value(self) -> Value {
Value::float(self)
}
fn from_value(v: &Value) -> Option<Self> {
v.as_float().map(|f| f as f32)
}
}
macro_rules! int_fields {
($($t:ty),*) => {$(
impl MessageField for $t {
fn to_value(self) -> Value {
Value::Int(i64::try_from(self).unwrap_or(i64::MAX))
}
fn from_value(v: &Value) -> Option<Self> {
v.as_int().and_then(|n| <$t>::try_from(n).ok())
}
}
)*};
}
int_fields!(i8, i16, i32, i64, isize, u8, u16, u32, u64, usize);
impl<T: MessageField> MessageField for Option<T> {
fn to_value(self) -> Value {
self.map_or(Value::Null, T::to_value)
}
fn from_value(v: &Value) -> Option<Self> {
match v {
Value::Null => Some(None),
v => T::from_value(v).map(Some),
}
}
}
impl<T: MessageField> MessageField for Vec<T> {
fn to_value(self) -> Value {
Value::List(self.into_iter().map(T::to_value).collect())
}
fn from_value(v: &Value) -> Option<Self> {
v.as_list()?.iter().map(T::from_value).collect()
}
}
impl<T: MessageField> MessageField for Box<T> {
fn to_value(self) -> Value {
(*self).to_value()
}
fn from_value(v: &Value) -> Option<Self> {
T::from_value(v).map(Box::new)
}
}
impl crate::input::UiEvent {
pub fn message<M>(&self) -> Option<M>
where
M: for<'a> TryFrom<&'a Value>,
{
M::try_from(&self.payload)
.ok()
.or_else(|| self.payload.get("tag").and_then(|t| M::try_from(t).ok()))
}
}