Skip to main content

elfo_core/message/
protocol.rs

1use std::ops::Deref;
2
3// See https://github.com/GoldsteinE/gh-blog/blob/master/const_deref_specialization/src/lib.md
4
5// Reexported in `elfo::_priv`.
6#[doc(hidden)]
7pub struct ProtocolExtractor;
8
9// Reexported in `elfo::_priv`.
10#[doc(hidden)]
11pub trait ProtocolHolder {
12    const PROTOCOL: Option<&'static str>;
13}
14
15// Reexported in `elfo::_priv`.
16#[doc(hidden)]
17pub struct DefaultProtocolHolder;
18
19impl ProtocolHolder for DefaultProtocolHolder {
20    // `None` means a crate's name is used.
21    const PROTOCOL: Option<&'static str> = None;
22}
23
24impl Deref for ProtocolExtractor {
25    type Target = DefaultProtocolHolder;
26
27    fn deref(&self) -> &Self::Target {
28        &DefaultProtocolHolder
29    }
30}
31
32impl DefaultProtocolHolder {
33    pub fn holder(&self) -> Self {
34        Self
35    }
36}
37
38// TODO: add examples.
39/// Overrides the protocol name for all messages in the current module.
40/// Can be used only once per module. Submodules inherit this override
41/// if `use super::*` is used.
42#[macro_export]
43macro_rules! set_protocol {
44    ($protocol:literal) => {
45        #[doc(hidden)]
46        struct _ElfoProtocolHolder;
47        impl $crate::_priv::ProtocolHolder for _ElfoProtocolHolder {
48            const PROTOCOL: Option<&'static str> = Some($protocol);
49        }
50
51        #[doc(hidden)]
52        trait _ElfoProtocolOverride {
53            fn holder(&self) -> _ElfoProtocolHolder {
54                _ElfoProtocolHolder
55            }
56        }
57        impl _ElfoProtocolOverride for $crate::_priv::ProtocolExtractor {}
58    };
59}
60
61#[doc(hidden)]
62#[macro_export]
63macro_rules! get_protocol {
64    () => {{
65        use $crate::_priv::{ProtocolExtractor, ProtocolHolder};
66
67        const fn extract_protocol_name<H: ProtocolHolder>(_: &impl FnOnce() -> H) -> &'static str {
68            // Get a protocol name from the relevant `set_protocol!` call.
69            if let Some(proto_override) = H::PROTOCOL {
70                return proto_override;
71            }
72
73            // If `set_protocol!` hasn't been used, take the package name.
74            if let Some(pkg_name) = option_env!("CARGO_PKG_NAME") {
75                return pkg_name;
76            }
77
78            panic!(
79                "if the crate is built without cargo, \
80                 the protocol must be set explicitly by calling `elfo::set_protocol!(..)`"
81            );
82        }
83
84        extract_protocol_name(&|| ProtocolExtractor.holder())
85    }};
86}