use yog_data::Schema;
use yog_ids as ids;
pub trait Message: Schema + Sized {
fn to_bytes(&self) -> Vec<u8> {
yog_data::frame::to_frame(self)
}
fn from_bytes(bytes: &[u8]) -> Option<Self> {
yog_data::frame::read_frame(bytes)
}
fn with_bytes<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
yog_data::frame::with_frame(self, f)
}
}
impl<T: Schema> Message for T {}
pub fn keyed(address: &str, key: &str) -> String {
format!("{address}{}{key}", ids::sep::PATH)
}
pub(crate) fn target(address: &str, key: Option<&str>) -> String {
match key {
Some(key) => keyed(address, key),
None => address.to_owned(),
}
}
pub(crate) fn branch(address: &'static str, prefix: &str) -> &'static str {
let path = address.strip_prefix(prefix).unwrap_or(address);
path.rsplit_once(ids::sep::PATH)
.map_or("", |(branch, _)| branch)
}
pub(crate) fn last_segment(path: &'static str) -> &'static str {
path.rfind(':')
.map_or(path, |i| path.split_at(i + 1).1.trim())
}
macro_rules! key {
() => {
None
};
($key:ident) => {
Some(stringify!($key))
};
}
macro_rules! key_value {
($self:ident,) => {
None
};
($self:ident, $key:ident) => {
Some(&$self.$key)
};
}
macro_rules! handle_method {
($name:ident, $reply:ty, [$($doc:tt)*]; uuid : $t:ty $(, $f:ident : $ft:ty)*) => {
handle_method!(@on $crate::Entity, uuid, $name, $reply, [$($doc)*]; $($f : $ft),*);
};
($name:ident, $reply:ty, [$($doc:tt)*]; player : $t:ty $(, $f:ident : $ft:ty)*) => {
handle_method!(@on $crate::Player, name, $name, $reply, [$($doc)*]; $($f : $ft),*);
};
($name:ident, $reply:ty, [$($doc:tt)*]; dim : $t:ty $(, $f:ident : $ft:ty)*) => {
handle_method!(@on $crate::World, dim, $name, $reply, [$($doc)*]; $($f : $ft),*);
};
(@on $handle:path, $key:ident, $name:ident, $reply:ty, [$($doc:tt)*]; $($f:ident : $ft:ty),*) => {
pastey::paste! {
impl $handle {
$($doc)*
pub fn [<$name:snake>](&self $(, $f: impl $crate::Arg<$ft>)*) -> $reply {
[<$name:snake>](&*self.$key $(, $f)*)
}
}
}
};
($($other:tt)*) => {};
}
macro_rules! actions {
($( $(#[$doc:meta])* $name:ident { $( $(#[$fattr:meta])* $field:ident : $fty:ty ),* $(,)? } -> $reply:ty = $address:expr, $host:literal; )*) => {
$(
$(#[$doc])*
#[derive(Debug, Clone, yog_data::Schema)]
pub struct $name { $( $(#[$fattr])* pub $field: $fty ),* }
impl $crate::Service for $name {
const ADDRESS: &'static str = $address;
const KEY: Option<&'static str> = None;
type Reply = $reply;
fn key(&self) -> Option<&str> {
None
}
fn address(&self) -> $crate::Address {
static AT: $crate::Cached = $crate::Cached::new();
AT.get($address)
}
}
pastey::paste! {
$(#[$doc])*
#[doc = ""]
#[doc = concat!("Calls [`", stringify!($name), "`]; what no one answers reads as the reply's default.")]
pub fn [<$name:snake>]($( $field: impl $crate::Arg<$fty> ),*) -> $reply {
$crate::call(&$name { $( $field: $crate::Arg::arg($field) ),* }).unwrap_or_default()
}
}
handle_method! { $name, $reply, [$(#[$doc])*]; $( $field : $fty ),* }
)*
pub(crate) fn actions() -> Vec<$crate::ServiceInfo> {
vec![$($crate::ServiceInfo {
domain: $crate::message::branch($address, yog_ids::service::PREFIX),
name: stringify!($name),
address: $address,
key: None,
shape: <$name as yog_data::Schema>::shape(),
reply: <$reply as yog_data::Schema>::shape(),
host: Some($host),
}),*]
}
};
}