use keelson_core::{FromValue, ToValue};
#[diagnostic::on_unimplemented(
message = "`{Self}` cannot be a keelson column type",
label = "not a column type",
note = "a column type binds in and reads back out: it must implement both `ToValue` and `FromValue`",
note = "for a newtype over one that already binds, `#[derive(Bind)]` (feature `macros`) or `bind_newtype!(Name(inner))` writes both impls"
)]
pub trait Bind: ToValue + FromValue + Send + 'static {}
#[diagnostic::do_not_recommend]
impl<T: ToValue + FromValue + Send + 'static> Bind for T {}
pub const fn assert_bind<T: Bind>() {}
#[macro_export]
macro_rules! bind_newtype {
($name:ident($inner:ty)) => {
impl $crate::__core::ToValue for $name {
fn to_value(self) -> $crate::__core::Value {
<$inner as $crate::__core::ToValue>::to_value(self.0)
}
}
impl $crate::__core::FromValue for $name {
fn from_value(
v: $crate::__core::Value,
) -> ::std::result::Result<Self, $crate::__core::Error> {
<$inner as $crate::__core::FromValue>::from_value(v).map($name)
}
}
};
}
#[cfg(test)]
mod tests {
use keelson_core::{FromValue, ToValue, Value};
use super::assert_bind;
#[derive(Debug, Clone, PartialEq)]
struct UserId(i64);
crate::bind_newtype!(UserId(i64));
const _: () = assert_bind::<UserId>();
#[test]
fn a_newtype_delegates_both_ways() {
assert_eq!(UserId(7).to_value(), Value::I64(7));
assert_eq!(UserId::from_value(Value::I64(7)).unwrap(), UserId(7));
assert_eq!(UserId::from_value(Value::I32(7)).unwrap(), UserId(7)); assert!(UserId::from_value(Value::Text("x".into())).is_err());
}
#[test]
fn options_of_newtypes_still_bind() {
assert_eq!(Some(UserId(1)).to_value(), Value::I64(1));
assert_eq!(Option::<UserId>::from_value(Value::Null).unwrap(), None);
}
}