1use std::fmt;
12use std::num::ParseIntError;
13use std::str::FromStr;
14
15use enum_as_inner::EnumAsInner;
16use serde::Deserialize;
17use serde::Serialize;
18use typeuri::Named;
19
20use crate::id::Label;
21use crate::id::Uid;
22
23#[derive(
25 Clone,
26 EnumAsInner,
27 PartialEq,
28 Eq,
29 Hash,
30 PartialOrd,
31 Ord,
32 Serialize,
33 Deserialize
34)]
35pub enum Port {
36 Ephemeral(u64),
38 Handler(Uid),
40 Control(ControlPort),
42}
43
44#[derive(
46 Clone,
47 Copy,
48 Debug,
49 PartialEq,
50 Eq,
51 Hash,
52 PartialOrd,
53 Ord,
54 Serialize,
55 Deserialize
56)]
57pub enum ControlPort {
58 Introspect,
60 Signal,
62 Status,
64}
65
66#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
68pub enum ControlPortParseError {
69 #[error("unknown control port {0:?}")]
71 Unknown(String),
72}
73
74#[derive(Debug, thiserror::Error)]
76pub enum PortParseError {
77 #[error("invalid ephemeral port: {0}")]
79 InvalidEphemeral(#[from] ParseIntError),
80 #[error("invalid handler port: {0}")]
82 InvalidHandler(#[from] crate::id::UidParseError),
83}
84
85impl Port {
86 pub fn ephemeral(index: u64) -> Self {
88 Self::Ephemeral(index)
89 }
90
91 pub fn handler<M: Named>() -> Self {
93 Self::handler_id(M::typehash(), Some(Label::strip(handler_label::<M>())))
94 }
95
96 pub fn handler_id(typehash: u64, label: Option<Label>) -> Self {
98 Self::Handler(Uid::Instance(typehash, label))
99 }
100
101 pub fn control(port: ControlPort) -> Self {
103 Self::Control(port)
104 }
105
106 pub fn ephemeral_index(&self) -> Option<u64> {
108 match self {
109 Self::Ephemeral(index) => Some(*index),
110 _ => None,
111 }
112 }
113
114 pub fn as_u64(&self) -> u64 {
116 match self {
117 Self::Ephemeral(index) => *index,
118 Self::Handler(uid) => uid.instance_value().unwrap_or_else(|| {
119 panic!("handler port should be identified by an instance uid: {uid}")
120 }),
121 Self::Control(ControlPort::Introspect) => 0,
122 Self::Control(ControlPort::Signal) => 1,
123 Self::Control(ControlPort::Status) => 2,
124 }
125 }
126}
127
128impl From<u64> for Port {
129 fn from(v: u64) -> Self {
130 Port::Ephemeral(v)
131 }
132}
133
134impl fmt::Display for Port {
135 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
136 match self {
137 Self::Ephemeral(index) => write!(f, "{index}"),
138 Self::Handler(uid) => write!(f, "{uid}"),
139 Self::Control(port) => write!(f, "{port}"),
140 }
141 }
142}
143
144impl fmt::Debug for Port {
145 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
146 match self {
147 Self::Ephemeral(index) => write!(f, "Port::Ephemeral({index})"),
148 Self::Handler(uid) => write!(f, "Port::Handler({uid})"),
149 Self::Control(port) => write!(f, "Port::Control({port:?})"),
150 }
151 }
152}
153
154impl FromStr for Port {
155 type Err = PortParseError;
156
157 fn from_str(s: &str) -> Result<Self, Self::Err> {
158 if s.bytes().all(|ch| ch.is_ascii_digit()) {
159 return Ok(Self::Ephemeral(s.parse()?));
160 }
161 let uid = s.parse()?;
162 match uid {
163 Uid::Instance(_, _) => Ok(Self::Handler(uid)),
164 Uid::Singleton(_) => Err(PortParseError::InvalidHandler(
165 crate::id::UidParseError::InvalidSyntax(s.to_string()),
166 )),
167 }
168 }
169}
170
171impl fmt::Display for ControlPort {
172 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
173 match self {
174 Self::Introspect => f.write_str("introspect"),
175 Self::Signal => f.write_str("signal"),
176 Self::Status => f.write_str("status"),
177 }
178 }
179}
180
181impl FromStr for ControlPort {
182 type Err = ControlPortParseError;
183
184 fn from_str(s: &str) -> Result<Self, Self::Err> {
185 match s {
186 "introspect" => Ok(Self::Introspect),
187 "signal" => Ok(Self::Signal),
188 "status" => Ok(Self::Status),
189 _ => Err(ControlPortParseError::Unknown(s.to_string())),
190 }
191 }
192}
193
194fn handler_label<M: Named>() -> &'static str {
195 M::typename()
196 .rsplit("::")
197 .next()
198 .unwrap_or_else(M::typename)
199}
200
201#[cfg(test)]
202mod tests {
203 use super::*;
204
205 struct TestMsg;
206
207 impl Named for TestMsg {
208 fn typename() -> &'static str {
209 "test::TestMsg"
210 }
211 }
212
213 #[test]
214 fn test_handler_port() {
215 let port = Port::handler::<TestMsg>();
216 assert!(port.is_handler(), "handler ports should be explicit");
217 assert_eq!(port.as_u64(), TestMsg::typehash());
218 }
219
220 #[test]
221 fn test_ephemeral_port() {
222 let port = Port::from(42);
223 assert!(port.is_ephemeral(), "decimal ports should be ephemeral");
224 assert!(!port.is_handler(), "ephemeral ports should not be handlers");
225 assert_eq!(port.as_u64(), 42);
226 }
227
228 #[test]
229 fn test_display_fromstr_roundtrip_ephemeral() {
230 let port = Port::from(12345);
231 assert_eq!(port.to_string(), "12345");
232 let parsed: Port = port.to_string().parse().expect("parse port");
233 assert_eq!(port, parsed);
234 }
235
236 #[test]
237 fn test_display_fromstr_roundtrip_handler() {
238 let port = Port::handler::<TestMsg>();
239 let s = port.to_string();
240 let parsed: Port = s.parse().expect("parse port");
241 assert_eq!(port, parsed);
242 }
243
244 #[test]
245 fn test_control_port_status() {
246 assert_eq!(ControlPort::Status.to_string(), "status");
247 assert_eq!(
248 "status".parse::<ControlPort>().expect("parse status"),
249 ControlPort::Status
250 );
251 assert_eq!(Port::control(ControlPort::Status).as_u64(), 2);
252 }
253
254 #[test]
255 fn test_debug() {
256 let port = Port::from(42);
257 assert_eq!(format!("{:?}", port), "Port::Ephemeral(42)");
258 }
259}