1#[macro_export]
30macro_rules! endpoint {
31 ($tyname:ident, $req:ty, $resp:ty) => {
32 endpoint!($tyname, $req, $resp, stringify!($tyname));
33 };
34 ($tyname:ident, $req:ty, $resp:ty, $path:expr,) => {
35 endpoint!($tyname, $req, $resp, $path)
36 };
37 ($tyname:ident, $req:ty, $resp:ty, $path:expr) => {
38 pub struct $tyname;
39
40 impl $crate::Endpoint for $tyname {
41 type Request = $req;
42 type Response = $resp;
43 const PATH: &'static str = $path;
44 const REQ_KEY: $crate::Key = $crate::Key::for_path::<$req>($path);
45 const RESP_KEY: $crate::Key = $crate::Key::for_path::<$resp>($path);
46 }
47 };
48}
49
50#[macro_export]
95macro_rules! endpoints {
96 (@ep_tys $([[$($meta:meta)?] $ep_name:ident])*) => {
97 $crate::endpoints!(@ep_tys omit_std=false; $([[$($meta)?] $ep_name])*)
98 };
99 (@ep_tys omit_std=true; $([[$($meta:meta)?] $ep_name:ident])*) => {
100 const {
101 const LISTS: &[&[&'static $crate::postcard_schema::schema::NamedType]] = &[
102 $(
103 $(#[$meta])?
104 $crate::unique_types!(<$ep_name as $crate::Endpoint>::Request),
105 $(#[$meta])?
106 $crate::unique_types!(<$ep_name as $crate::Endpoint>::Response),
107 )*
108 ];
109
110 const TTL_COUNT: usize = $crate::uniques::total_len(LISTS);
111 const BIG_RPT: ([Option<&'static $crate::postcard_schema::schema::NamedType>; TTL_COUNT], usize) = $crate::uniques::merge_nty_lists(LISTS);
112 const SMALL_RPT: [&'static $crate::postcard_schema::schema::NamedType; BIG_RPT.1] = $crate::uniques::cruncher(BIG_RPT.0.as_slice());
113 SMALL_RPT.as_slice()
114 }
115 };
116 (@ep_tys omit_std=false; $([[$($meta:meta)?] $ep_name:ident])*) => {
117 const {
118 const USER_TYS: &[&'static $crate::postcard_schema::schema::NamedType] =
119 $crate::endpoints!(@ep_tys omit_std=true; $([[$($meta)?] $ep_name])*);
120 const STD_TYS: &[&'static $crate::postcard_schema::schema::NamedType]
121 = $crate::standard_icd::STANDARD_ICD_ENDPOINTS.types;
122
123 const BOTH: &[&[&'static $crate::postcard_schema::schema::NamedType]] = &[
124 USER_TYS, STD_TYS,
125 ];
126 const TTL_COUNT: usize = $crate::uniques::total_len(BOTH);
127 const BIG_RPT: ([Option<&'static $crate::postcard_schema::schema::NamedType>; TTL_COUNT], usize) = $crate::uniques::merge_nty_lists(BOTH);
128 const SMALL_RPT: [&'static $crate::postcard_schema::schema::NamedType; BIG_RPT.1] = $crate::uniques::cruncher(BIG_RPT.0.as_slice());
129 SMALL_RPT.as_slice()
130 }
131 };
132 (@ep_eps $([[$($meta:meta)?] $ep_name:ident])*) => {
133 $crate::endpoints!(@ep_eps omit_std=false; $([[$($meta)?] $ep_name])*)
134 };
135 (@ep_eps omit_std=true; $([[$($meta:meta)?] $ep_name:ident])*) => {
136 &[
137 $(
138 $(#[$meta])?
139 (
140 <$ep_name as $crate::Endpoint>::PATH,
141 <$ep_name as $crate::Endpoint>::REQ_KEY,
142 <$ep_name as $crate::Endpoint>::RESP_KEY,
143 ),
144 )*
145 ]
146 };
147 (@ep_eps omit_std=false; $([[$($meta:meta)?] $ep_name:ident])*) => {
148 const {
149 const USER_EPS: &[(&str, $crate::Key, $crate::Key)] =
150 $crate::endpoints!(@ep_eps omit_std=true; $([[$($meta)?] $ep_name])*);
151 const NULL_KEY: $crate::Key = unsafe { $crate::Key::from_bytes([0u8; 8]) };
152 const STD_EPS: &[(&str, $crate::Key, $crate::Key)] =
153 $crate::standard_icd::STANDARD_ICD_ENDPOINTS.endpoints;
154
155 $crate::concat_arrays! {
156 init = ("", NULL_KEY, NULL_KEY);
157 ty = (&str, $crate::Key, $crate::Key);
158 [STD_EPS, USER_EPS]
159 }
160 }
161 };
162 (
163 list = $list_name:ident;
164 $(omit_std = $omit:tt;)?
165 | EndpointTy | RequestTy | ResponseTy | Path | $( Cfg |)?
166 | $(-)* | $(-)* | $(-)* | $(-)* | $($(-)* |)?
167 $( | $ep_name:ident | $req_ty:tt $(< $($req_lt:lifetime),+ >)? | $resp_ty:tt $(< $($resp_lt:lifetime),+ >)? | $path_str:literal | $($meta:meta)? $(|)? )*
168 ) => {
169 $(
171 $(#[$meta])?
173 pub struct $ep_name < $($($req_lt,)+)? $($($resp_lt,)+)? > {
174 $(
175 _plt_req: core::marker::PhantomData<($(& $req_lt (),)+)>,
176 )?
177 $(
178 _plt_resp: core::marker::PhantomData<($(& $resp_lt (),)+)>,
179 )?
180 _priv: core::marker::PhantomData<()>,
181 }
182
183 $(#[$meta])?
184 impl < $($($req_lt,)+)? $($($resp_lt,)+)? > $crate::Endpoint for $ep_name < $($($req_lt,)+)? $($($resp_lt,)+)? > {
185 type Request = $req_ty $(< $($req_lt,)+ >)?;
186 type Response = $resp_ty $(< $($resp_lt,)+ >)?;
187 const PATH: &'static str = $path_str;
188 const REQ_KEY: $crate::Key = $crate::Key::for_path::<$req_ty>($path_str);
189 const RESP_KEY: $crate::Key = $crate::Key::for_path::<$resp_ty>($path_str);
190 }
191 )*
192
193 pub const $list_name: $crate::EndpointMap = $crate::EndpointMap {
195 types: $crate::endpoints!(@ep_tys $(omit_std = $omit;)? $([[$($meta)?] $ep_name])*),
196 endpoints: $crate::endpoints!(@ep_eps $(omit_std = $omit;)? $([[$($meta)?] $ep_name])*),
197 };
198 };
199}
200
201#[macro_export]
224macro_rules! topic {
225 ($tyname:ident, $msg:ty) => {
226 topic!($tyname, $msg, stringify!($tyname));
227 };
228 ($tyname:ident, $msg:ty, $path:expr,) => {
229 topic!($tyname, $msg, $path)
230 };
231 ($tyname:ident, $msg:ty, $path:expr) => {
232 pub struct $tyname;
236
237 impl $crate::Topic for $tyname {
238 type Message = $msg;
239 const PATH: &'static str = $path;
240 const TOPIC_KEY: $crate::Key = $crate::Key::for_path::<$msg>($path);
241 }
242 };
243}
244
245#[macro_export]
279macro_rules! topics {
280 (@tp_tys ( $dir:expr ) $([[$($meta:meta)?] $tp_name:ident])*) => {
281 $crate::topics!(@tp_tys ( $dir ) omit_std=false; $([[$($meta)?] $tp_name])*)
282 };
283 (@tp_tys ( $dir:expr ) omit_std=true; $([[$($meta:meta)?] $tp_name:ident])*) => {
284 const {
285 const LISTS: &[&[&'static $crate::postcard_schema::schema::NamedType]] = &[
286 $(
287 $(#[$meta])?
288 $crate::unique_types!(<$tp_name as $crate::Topic>::Message),
289 )*
290 ];
291
292 const TTL_COUNT: usize = $crate::uniques::total_len(LISTS);
293 const BIG_RPT: ([Option<&'static $crate::postcard_schema::schema::NamedType>; TTL_COUNT], usize) = $crate::uniques::merge_nty_lists(LISTS);
294 const SMALL_RPT: [&'static $crate::postcard_schema::schema::NamedType; BIG_RPT.1] = $crate::uniques::cruncher(BIG_RPT.0.as_slice());
295 SMALL_RPT.as_slice()
296 }
297 };
298 (@tp_tys ( $dir:expr ) omit_std=false; $([[$($meta:meta)?] $tp_name:ident])*) => {
299 const {
300 const USER_TYS: &[&'static $crate::postcard_schema::schema::NamedType] =
301 $crate::topics!(@tp_tys ( $dir ) omit_std=true; $([[$($meta)?] $tp_name])*);
302 const STD_TYS: &[&'static $crate::postcard_schema::schema::NamedType] = const {
303 match $dir {
304 $crate::TopicDirection::ToServer => $crate::standard_icd::STANDARD_ICD_TOPICS_IN.types,
305 $crate::TopicDirection::ToClient => $crate::standard_icd::STANDARD_ICD_TOPICS_OUT.types,
306 }
307 };
308
309 const BOTH: &[&[&'static $crate::postcard_schema::schema::NamedType]] = &[
310 STD_TYS, USER_TYS,
311 ];
312 const TTL_COUNT: usize = $crate::uniques::total_len(BOTH);
313 const BIG_RPT: ([Option<&'static $crate::postcard_schema::schema::NamedType>; TTL_COUNT], usize) = $crate::uniques::merge_nty_lists(BOTH);
314 const SMALL_RPT: [&'static $crate::postcard_schema::schema::NamedType; BIG_RPT.1] = $crate::uniques::cruncher(BIG_RPT.0.as_slice());
315 SMALL_RPT.as_slice()
316 }
317 };
318 (@tp_tps ( $dir:expr ) $([[$($meta:meta)?] $tp_name:ident])*) => {
319 $crate::topics!(@tp_tps ( $dir ) omit_std=false; $([[$($meta)?] $tp_name])*)
320 };
321 (@tp_tps ( $dir:expr ) omit_std=true; $([[$($meta:meta)?] $tp_name:ident])*) => {
322 &[
323 $(
324 $(#[$meta])?
325 (
326 <$tp_name as $crate::Topic>::PATH,
327 <$tp_name as $crate::Topic>::TOPIC_KEY,
328 ),
329 )*
330 ]
331 };
332 (@tp_tps ( $dir:expr ) omit_std=false; $([[$($meta:meta)?] $tp_name:ident])*) => {
333 const {
334 const USER_TPS: &[(&str, $crate::Key)] =
335 $crate::topics!(@tp_tps ( $dir ) omit_std=true; $([[$($meta)?] $tp_name])*);
336 const NULL_KEY: $crate::Key = unsafe { $crate::Key::from_bytes([0u8; 8]) };
337 const STD_TPS: &[(&str, $crate::Key)] = const {
338 match $dir {
339 $crate::TopicDirection::ToServer => $crate::standard_icd::STANDARD_ICD_TOPICS_IN.topics,
340 $crate::TopicDirection::ToClient => $crate::standard_icd::STANDARD_ICD_TOPICS_OUT.topics,
341 }
342 };
343
344 $crate::concat_arrays! {
345 init = ("", NULL_KEY);
346 ty = (&str, $crate::Key);
347 [STD_TPS, USER_TPS]
348 }
349 }
350 };
351 (
352 list = $list_name:ident;
353 direction = $direction:expr;
354 $(omit_std = $omit:tt;)?
355 | TopicTy | MessageTy | Path | $( Cfg |)?
356 | $(-)* | $(-)* | $(-)* | $($(-)* |)?
357 $(| $tp_name:ident | $msg_ty:tt $(< $($msg_lt:lifetime),+ >)? | $path_str:literal | $($meta:meta)? $(|)?)*
358 ) => {
359 $(
361 $(#[$meta])?
365 pub struct $tp_name $(< $($msg_lt,)+ >)? {
366 $(
367 _plt: core::marker::PhantomData<($(& $msg_lt (),)+)>,
368 )?
369 _priv: core::marker::PhantomData<()>,
370 }
371
372 $(#[$meta])?
373 impl $(< $($msg_lt),+ >)? $crate::Topic for $tp_name $(< $($msg_lt,)+ >)? {
374 type Message = $msg_ty $(< $($msg_lt,)+ >)?;
375 const PATH: &'static str = $path_str;
376 const TOPIC_KEY: $crate::Key = $crate::Key::for_path::<$msg_ty>($path_str);
377 }
378 )*
379
380 pub const $list_name: $crate::TopicMap = $crate::TopicMap {
382 direction: $direction,
383 types: $crate::topics!(@tp_tys ( $direction ) $(omit_std = $omit;)? $([[$($meta)?] $tp_name])*),
384 topics: $crate::topics!(@tp_tps ( $direction ) $(omit_std = $omit;)? $([[$($meta)?] $tp_name])*),
385 };
386 };
387}
388
389#[macro_export]
391macro_rules! concat_arrays {
392 (
393 init = $init:expr;
394 ty = $tyname:ty;
395 [$($arr:ident),+]
396 ) => {
397 const {
398 const SLI: &[&[$tyname]] = &[
399 $($arr,)+
400 ];
401 const LEN: usize = $crate::uniques::total_len(SLI);
402 const ARR: [$tyname; LEN] = $crate::uniques::combine_with_copy(SLI, $init);
403
404 ARR.as_slice()
405 }
406 };
407}
408
409#[cfg(test)]
410mod concat_test {
411 #[test]
412 fn concats() {
413 const A: &[u32] = &[1, 2, 3];
414 const B: &[u32] = &[4, 5, 6];
415 const BOTH: &[u32] = concat_arrays!(
416 init = 0xFFFF_FFFF;
417 ty = u32;
418 [A, B]
419 );
420 assert_eq!(BOTH, [1, 2, 3, 4, 5, 6]);
421 }
422}
423
424#[macro_export]
426macro_rules! sender_fmt {
427 ($sender:ident, $($arg:tt)*) => {
428 $sender.log_fmt(format_args!($($arg)*))
429 };
430 ($($arg:tt)*) => {
431 compile_error!("You must pass the sender to `sender_log`!");
432 }
433}
434
435#[cfg(test)]
436mod endpoints_test {
437 use postcard_schema::{schema::owned::OwnedNamedType, Schema};
438 use serde::{Deserialize, Serialize};
439
440 #[derive(Serialize, Deserialize, Schema)]
441 pub struct AReq(pub u8);
442 #[derive(Serialize, Deserialize, Schema)]
443 pub struct AResp(pub u16);
444 #[derive(Serialize, Deserialize, Schema)]
445 pub struct BTopic(pub u32);
446
447 endpoints! {
448 list = ENDPOINT_LIST;
449 | EndpointTy | RequestTy | ResponseTy | Path |
450 | ---------- | --------- | ---------- | ---- |
451 | AlphaEndpoint1 | AReq | AResp | "test/alpha1" |
452 | AlphaEndpoint2 | AReq | AResp | "test/alpha2" |
453 | AlphaEndpoint3 | AReq | AResp | "test/alpha3" |
454 }
455
456 topics! {
457 list = TOPICS_IN_LIST;
458 direction = crate::TopicDirection::ToServer;
459 | TopicTy | MessageTy | Path |
460 | ---------- | --------- | ---- |
461 | BetaTopic1 | BTopic | "test/in/beta1" |
462 | BetaTopic2 | BTopic | "test/in/beta2" |
463 | BetaTopic3 | BTopic | "test/in/beta3" |
464 }
465
466 topics! {
467 list = TOPICS_OUT_LIST;
468 direction = crate::TopicDirection::ToClient;
469 | TopicTy | MessageTy | Path |
470 | ---------- | --------- | ---- |
471 | BetaTopic4 | BTopic | "test/out/beta1" |
472 }
473
474 #[test]
475 fn eps() {
476 for ep in ENDPOINT_LIST.types {
477 println!("{}", OwnedNamedType::from(*ep));
478 }
479 assert_eq!(ENDPOINT_LIST.types.len(), 3);
480 for ep in ENDPOINT_LIST.endpoints {
481 println!("{}", ep.0);
482 }
483 assert_eq!(ENDPOINT_LIST.endpoints.len(), 5);
484 }
485
486 #[test]
487 fn tps() {
488 for tp in TOPICS_IN_LIST.types {
489 println!("TY IN: {}", OwnedNamedType::from(*tp));
490 }
491 for tp in TOPICS_IN_LIST.topics {
492 println!("TP IN: {}", tp.0);
493 }
494 for tp in TOPICS_OUT_LIST.types {
495 println!("TY OUT: {}", OwnedNamedType::from(*tp));
496 }
497 for tp in TOPICS_OUT_LIST.topics {
498 println!("TP OUT: {}", tp.0);
499 }
500 assert_eq!(TOPICS_IN_LIST.types.len(), 1);
501 assert_eq!(TOPICS_IN_LIST.topics.len(), 3);
502 assert_eq!(TOPICS_OUT_LIST.types.len(), 5);
503 assert_eq!(TOPICS_OUT_LIST.topics.len(), 3);
504 }
505}