1#[rustfmt::skip]
4#[allow(clippy::all)]
5mod proto {
6 include!("proto/iscp2.v1.rs");
7
8 #[path = "iscp2.v1.extensions.rs"]
9 pub mod extensions;
10}
11
12pub use proto::*;
13
14impl DataId {
15 pub fn new<SN: ToString, ST: ToString>(name: SN, type_: ST) -> Self {
16 Self {
17 name: name.to_string(),
18 type_: type_.to_string(),
19 }
20 }
21
22 pub fn validate(&self) -> Result<(), DataIdParseError> {
23 let list = ['#', '+', ':'];
24 for &c in list.iter() {
25 if self.name.contains(c) || self.type_.contains(c) {
26 return Err(DataIdParseError::InvalidChar(c));
27 }
28 }
29
30 Ok(())
31 }
32}
33
34impl std::str::FromStr for DataId {
35 type Err = DataIdParseError;
36
37 fn from_str(s: &str) -> Result<Self, Self::Err> {
38 let mut split = s.split(':');
39 let Some(type_) = split.next() else {
40 return Err(DataIdParseError::SplitterNotFound);
41 };
42 let Some(name) = split.next() else {
43 return Err(DataIdParseError::SplitterNotFound);
44 };
45 if split.next().is_some() {
46 return Err(DataIdParseError::MultipleSplitterFound);
47 }
48
49 let id = Self {
50 type_: type_.to_owned(),
51 name: name.to_owned(),
52 };
53 id.validate()?;
54 Ok(id)
55 }
56}
57
58impl std::fmt::Display for DataId {
59 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
60 write!(f, "{}:{}", self.type_, self.name)
61 }
62}
63
64#[derive(Clone, PartialEq, Eq, Debug, thiserror::Error)]
65pub enum DataIdParseError {
66 #[error("splitter ':' not found")]
67 SplitterNotFound,
68 #[error("multiple splitter found")]
69 MultipleSplitterFound,
70 #[error("include invalid character")]
71 InvalidChar(char),
72}
73
74#[derive(Debug, thiserror::Error)]
75#[error("message convert error")]
76pub struct MessageConvertError(pub Message);
77
78macro_rules! impl_from {
79 ($($member:ident,)*) => {
80 $(
81 impl From<$member> for Message {
82 fn from(m: $member) -> Self {
83 Message {
84 message: Some(proto::message::Message::$member(m)),
85 }
86 }
87 }
88 impl TryFrom<Message> for $member {
89 type Error = MessageConvertError;
90 fn try_from(m: Message) -> core::result::Result<Self, Self::Error> {
91 match m.message {
92 Some(proto::message::Message::$member(a)) => Ok(a),
93 _ => Err(MessageConvertError(m)),
94 }
95 }
96 }
97 )*
98 }
99}
100
101impl_from!(
102 ConnectRequest,
103 ConnectResponse,
104 Disconnect,
105 DownstreamCall,
106 DownstreamChunk,
107 DownstreamChunkAck,
108 DownstreamChunkAckComplete,
109 DownstreamCloseRequest,
110 DownstreamCloseResponse,
111 DownstreamMetadata,
112 DownstreamMetadataAck,
113 DownstreamOpenRequest,
114 DownstreamOpenResponse,
115 DownstreamResumeRequest,
116 DownstreamResumeResponse,
117 Ping,
118 Pong,
119 UpstreamCall,
120 UpstreamCallAck,
121 UpstreamChunk,
122 UpstreamChunkAck,
123 UpstreamCloseRequest,
124 UpstreamCloseResponse,
125 UpstreamMetadata,
126 UpstreamMetadataAck,
127 UpstreamOpenRequest,
128 UpstreamOpenResponse,
129 UpstreamResumeRequest,
130 UpstreamResumeResponse,
131);
132
133mod private {
134 pub trait Sealed {}
135}
136
137pub trait HasRequestId: private::Sealed {
138 fn request_id(&self) -> u32;
139 fn set_request_id(&mut self, request_id: u32);
140}
141
142macro_rules! impl_has_request_id {
143 ($($member:ident,)*) => {
144 $(
145 impl private::Sealed for $member {}
146 impl HasRequestId for $member {
147 fn request_id(&self) -> u32 {
148 self.request_id
149 }
150
151 fn set_request_id(&mut self, request_id: u32) {
152 self.request_id = request_id;
153 }
154 }
155 )*
156 }
157}
158
159impl_has_request_id!(
160 ConnectRequest,
161 ConnectResponse,
162 DownstreamCloseRequest,
163 DownstreamCloseResponse,
164 DownstreamMetadata,
165 DownstreamMetadataAck,
166 DownstreamOpenRequest,
167 DownstreamOpenResponse,
168 DownstreamResumeRequest,
169 DownstreamResumeResponse,
170 Ping,
171 Pong,
172 UpstreamCloseRequest,
173 UpstreamCloseResponse,
174 UpstreamMetadata,
175 UpstreamMetadataAck,
176 UpstreamOpenRequest,
177 UpstreamOpenResponse,
178 UpstreamResumeRequest,
179 UpstreamResumeResponse,
180);
181
182pub trait HasResultCode: private::Sealed {
183 fn result_code(&self) -> Option<ResultCode>;
184 fn result_string(&self) -> &str;
185}
186
187macro_rules! impl_has_result_code {
188 ($($member:ident,)*) => {
189 $(
190 impl HasResultCode for $member {
191 fn result_code(&self) -> Option<ResultCode> {
192 self.result_code.try_into().ok()
193 }
194
195 fn result_string(&self) -> &str {
196 &self.result_string
197 }
198 }
199 )*
200 }
201}
202
203impl_has_result_code!(
204 ConnectResponse,
205 DownstreamCloseResponse,
206 DownstreamMetadataAck,
207 DownstreamOpenResponse,
208 DownstreamResumeResponse,
209 UpstreamCloseResponse,
210 UpstreamMetadataAck,
211 UpstreamOpenResponse,
212 UpstreamResumeResponse,
213);
214
215pub trait RequestMessage: Into<Message> + HasRequestId {
216 type Response: TryFrom<Message> + HasRequestId + HasResultCode;
217}
218
219impl RequestMessage for ConnectRequest {
220 type Response = ConnectResponse;
221}
222impl RequestMessage for DownstreamCloseRequest {
223 type Response = DownstreamCloseResponse;
224}
225impl RequestMessage for DownstreamMetadata {
226 type Response = DownstreamMetadataAck;
227}
228impl RequestMessage for DownstreamOpenRequest {
229 type Response = DownstreamOpenResponse;
230}
231impl RequestMessage for DownstreamResumeRequest {
232 type Response = DownstreamResumeResponse;
233}
234impl RequestMessage for UpstreamCloseRequest {
235 type Response = UpstreamCloseResponse;
236}
237impl RequestMessage for UpstreamMetadata {
238 type Response = UpstreamMetadataAck;
239}
240impl RequestMessage for UpstreamOpenRequest {
241 type Response = UpstreamOpenResponse;
242}
243impl RequestMessage for UpstreamResumeRequest {
244 type Response = UpstreamResumeResponse;
245}
246
247impl Message {
248 pub fn request_id(&self) -> Option<u32> {
249 use proto::message::Message::*;
250 let request_id = match self.message.as_ref()? {
251 ConnectRequest(a) => a.request_id,
252 ConnectResponse(a) => a.request_id,
253 DownstreamCloseRequest(a) => a.request_id,
254 DownstreamCloseResponse(a) => a.request_id,
255 DownstreamMetadata(a) => a.request_id,
256 DownstreamMetadataAck(a) => a.request_id,
257 DownstreamOpenRequest(a) => a.request_id,
258 DownstreamOpenResponse(a) => a.request_id,
259 DownstreamResumeRequest(a) => a.request_id,
260 DownstreamResumeResponse(a) => a.request_id,
261 Ping(a) => a.request_id,
262 Pong(a) => a.request_id,
263 UpstreamCloseRequest(a) => a.request_id,
264 UpstreamCloseResponse(a) => a.request_id,
265 UpstreamMetadata(a) => a.request_id,
266 UpstreamMetadataAck(a) => a.request_id,
267 UpstreamOpenRequest(a) => a.request_id,
268 UpstreamOpenResponse(a) => a.request_id,
269 UpstreamResumeRequest(a) => a.request_id,
270 UpstreamResumeResponse(a) => a.request_id,
271 _ => {
272 return None;
273 }
274 };
275 Some(request_id)
276 }
277}
278
279#[cfg(test)]
280mod test {
281 use super::*;
282 use std::str::FromStr;
283
284 #[test]
285 fn data_id_validate() {
286 assert!(DataId::new("name", "type").validate().is_ok());
287 assert!(DataId::new("na:me", "type").validate().is_err());
288 assert!(DataId::new("na+me", "type").validate().is_err());
289 assert!(DataId::new("na#me", "type").validate().is_err());
290 assert!(DataId::new("name", "ty:pe").validate().is_err());
291 assert!(DataId::new("name", "ty+pe").validate().is_err());
292 assert!(DataId::new("name", "ty#pe").validate().is_err());
293 }
294
295 #[test]
296 fn data_id_parse_str() {
297 assert_eq!(
298 DataId::from_str("type:name").unwrap(),
299 DataId::new("name", "type"),
300 );
301 assert!(DataId::from_str("type-name").is_err());
302 assert!(DataId::from_str("ty:pe:name").is_err());
303 assert!(DataId::from_str("ty#pe:name").is_err());
304 assert!(DataId::from_str("type:na+me").is_err());
305 }
306
307 #[test]
308 fn data_id_format() {
309 let id = DataId::from_str("type:name").unwrap();
310 assert_eq!(format!("{id}"), "type:name");
311 }
312}