yog_event/wire/
message.rs1use 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 let mut packer = PACKER.take().unwrap_or_default();
26 let out = packer.with_frame(self, f);
27 PACKER.set(Some(packer));
28 out
29 }
30}
31
32impl<T: Schema> Message for T {}
33
34thread_local! {
35 static PACKER: std::cell::Cell<Option<yog_data::frame::Packer>> =
37 const { std::cell::Cell::new(None) };
38}
39
40pub fn keyed(address: &str, key: &str) -> String {
42 format!("{address}{}{key}", ids::sep::PATH)
43}
44
45pub(crate) fn target(address: &str, key: Option<&str>) -> String {
47 match key {
48 Some(key) => keyed(address, key),
49 None => address.to_owned(),
50 }
51}
52
53pub(crate) fn branch(address: &'static str, prefix: &str) -> &'static str {
56 let path = address.strip_prefix(prefix).unwrap_or(address);
57 path.rsplit_once(ids::sep::PATH)
58 .map_or("", |(branch, _)| branch)
59}
60
61pub(crate) fn last_segment(path: &'static str) -> &'static str {
63 path.rfind(':')
64 .map_or(path, |i| path.split_at(i + 1).1.trim())
65}
66
67macro_rules! key {
69 () => {
70 None
71 };
72 ($key:ident) => {
73 Some(stringify!($key))
74 };
75}
76
77macro_rules! key_value {
79 ($self:ident,) => {
80 None
81 };
82 ($self:ident, $key:ident) => {
83 Some(&$self.$key)
84 };
85}
86
87macro_rules! handle_method {
91 ($name:ident, $reply:ty, [$($doc:tt)*]; uuid : $t:ty $(, $f:ident : $ft:ty)*) => {
92 handle_method!(@on $crate::Entity, uuid, $name, $reply, [$($doc)*]; $($f : $ft),*);
93 };
94 ($name:ident, $reply:ty, [$($doc:tt)*]; player : $t:ty $(, $f:ident : $ft:ty)*) => {
95 handle_method!(@on $crate::Player, name, $name, $reply, [$($doc)*]; $($f : $ft),*);
96 };
97 ($name:ident, $reply:ty, [$($doc:tt)*]; dim : $t:ty $(, $f:ident : $ft:ty)*) => {
98 handle_method!(@on $crate::World, dim, $name, $reply, [$($doc)*]; $($f : $ft),*);
99 };
100 (@on $handle:path, $key:ident, $name:ident, $reply:ty, [$($doc:tt)*]; $($f:ident : $ft:ty),*) => {
101 pastey::paste! {
102 impl $handle {
103 $($doc)*
104 pub fn [<$name:snake>](&self $(, $f: impl $crate::Arg<$ft>)*) -> $reply {
105 [<$name:snake>](&*self.$key $(, $f)*)
106 }
107 }
108 }
109 };
110 ($($other:tt)*) => {};
111}
112
113macro_rules! actions {
117 ($( $(#[$doc:meta])* $name:ident { $( $(#[$fattr:meta])* $field:ident : $fty:ty ),* $(,)? } -> $reply:ty = $address:expr, $host:literal; )*) => {
118 $(
119 $(#[$doc])*
120 #[derive(Debug, Clone, yog_data::Schema)]
121 pub struct $name { $( $(#[$fattr])* pub $field: $fty ),* }
122
123 impl $crate::Service for $name {
124 const ADDRESS: &'static str = $address;
125 const KEY: Option<&'static str> = None;
126 type Reply = $reply;
127
128 fn key(&self) -> Option<&str> {
129 None
130 }
131
132 fn address(&self) -> $crate::Address {
133 static AT: $crate::Cached = $crate::Cached::new();
134 AT.get($address)
135 }
136 }
137
138 pastey::paste! {
139 $(#[$doc])*
140 #[doc = ""]
141 #[doc = concat!("Calls [`", stringify!($name), "`]; what no one answers reads as the reply's default.")]
142 pub fn [<$name:snake>]($( $field: impl $crate::Arg<$fty> ),*) -> $reply {
143 $crate::call(&$name { $( $field: $crate::Arg::arg($field) ),* }).unwrap_or_default()
144 }
145 }
146
147 handle_method! { $name, $reply, [$(#[$doc])*]; $( $field : $fty ),* }
148 )*
149
150 pub(crate) fn actions() -> Vec<$crate::ServiceInfo> {
152 vec![$($crate::ServiceInfo {
153 domain: $crate::wire::message::branch($address, yog_ids::service::PREFIX),
154 name: stringify!($name),
155 address: $address,
156 key: None,
157 shape: <$name as yog_data::Schema>::shape(),
158 reply: <$reply as yog_data::Schema>::shape(),
159 host: Some($host),
160 }),*]
161 }
162 };
163}