Skip to main content

iscp/message/
mod.rs

1//! iSCPメッセージ型の定義
2
3#[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}