1use yog_data::Schema;
5use yog_ids as ids;
6
7pub trait Message: Schema + Sized {
10 fn to_bytes(&self) -> Vec<u8> {
12 yog_data::frame::to_frame(self)
13 }
14
15 fn from_bytes(bytes: &[u8]) -> Option<Self> {
17 yog_data::frame::read_frame(bytes)
18 }
19
20 fn with_bytes<R>(&self, f: impl FnOnce(&[u8]) -> R) -> R {
23 yog_data::frame::with_frame(self, f)
24 }
25}
26
27impl<T: Schema> Message for T {}
28
29pub fn keyed(address: &str, key: &str) -> String {
31 format!("{address}{}{key}", ids::sep::PATH)
32}
33
34pub(crate) fn target(address: &str, key: Option<&str>) -> String {
36 match key {
37 Some(key) => keyed(address, key),
38 None => address.to_owned(),
39 }
40}
41
42pub(crate) fn branch(address: &'static str, prefix: &str) -> &'static str {
45 let path = address.strip_prefix(prefix).unwrap_or(address);
46 path.rsplit_once(ids::sep::PATH)
47 .map_or("", |(branch, _)| branch)
48}
49
50pub(crate) fn last_segment(path: &'static str) -> &'static str {
52 path.rfind(':')
53 .map_or(path, |i| path.split_at(i + 1).1.trim())
54}
55
56macro_rules! key {
58 () => {
59 None
60 };
61 ($key:ident) => {
62 Some(stringify!($key))
63 };
64}
65
66macro_rules! key_value {
68 ($self:ident,) => {
69 None
70 };
71 ($self:ident, $key:ident) => {
72 Some(&$self.$key)
73 };
74}
75
76macro_rules! handle_method {
80 ($name:ident, $reply:ty, [$($doc:tt)*]; uuid : $t:ty $(, $f:ident : $ft:ty)*) => {
81 handle_method!(@on $crate::Entity, uuid, $name, $reply, [$($doc)*]; $($f : $ft),*);
82 };
83 ($name:ident, $reply:ty, [$($doc:tt)*]; player : $t:ty $(, $f:ident : $ft:ty)*) => {
84 handle_method!(@on $crate::Player, name, $name, $reply, [$($doc)*]; $($f : $ft),*);
85 };
86 ($name:ident, $reply:ty, [$($doc:tt)*]; dim : $t:ty $(, $f:ident : $ft:ty)*) => {
87 handle_method!(@on $crate::World, dim, $name, $reply, [$($doc)*]; $($f : $ft),*);
88 };
89 (@on $handle:path, $key:ident, $name:ident, $reply:ty, [$($doc:tt)*]; $($f:ident : $ft:ty),*) => {
90 pastey::paste! {
91 impl $handle {
92 $($doc)*
93 pub fn [<$name:snake>](&self $(, $f: impl $crate::Arg<$ft>)*) -> $reply {
94 [<$name:snake>](&*self.$key $(, $f)*)
95 }
96 }
97 }
98 };
99 ($($other:tt)*) => {};
100}
101
102macro_rules! actions {
106 ($( $(#[$doc:meta])* $name:ident { $( $(#[$fattr:meta])* $field:ident : $fty:ty ),* $(,)? } -> $reply:ty = $address:expr, $host:literal; )*) => {
107 $(
108 $(#[$doc])*
109 #[derive(Debug, Clone, yog_data::Schema)]
110 pub struct $name { $( $(#[$fattr])* pub $field: $fty ),* }
111
112 impl $crate::Service for $name {
113 const ADDRESS: &'static str = $address;
114 const KEY: Option<&'static str> = None;
115 type Reply = $reply;
116
117 fn key(&self) -> Option<&str> {
118 None
119 }
120
121 fn address(&self) -> $crate::Address {
122 static AT: $crate::Cached = $crate::Cached::new();
123 AT.get($address)
124 }
125 }
126
127 pastey::paste! {
128 $(#[$doc])*
129 #[doc = ""]
130 #[doc = concat!("Calls [`", stringify!($name), "`]; what no one answers reads as the reply's default.")]
131 pub fn [<$name:snake>]($( $field: impl $crate::Arg<$fty> ),*) -> $reply {
132 $crate::call(&$name { $( $field: $crate::Arg::arg($field) ),* }).unwrap_or_default()
133 }
134 }
135
136 handle_method! { $name, $reply, [$(#[$doc])*]; $( $field : $fty ),* }
137 )*
138
139 pub(crate) fn actions() -> Vec<$crate::ServiceInfo> {
141 vec![$($crate::ServiceInfo {
142 domain: $crate::message::branch($address, yog_ids::service::PREFIX),
143 name: stringify!($name),
144 address: $address,
145 key: None,
146 shape: <$name as yog_data::Schema>::shape(),
147 reply: <$reply as yog_data::Schema>::shape(),
148 host: Some($host),
149 }),*]
150 }
151 };
152}