1use gbp::CodecError;
4use gbp_core::PayloadCodec;
5use serde::{Deserialize, Serialize};
6use serde_bytes::ByteBuf;
7
8#[derive(Clone, Debug, Serialize, Deserialize)]
10pub struct GspSignal {
11 #[serde(rename = "t")]
13 pub signal_type: u32,
14 #[serde(rename = "rid")]
16 pub request_id: u32,
17 #[serde(rename = "sid")]
19 pub sender_id: u32,
20 #[serde(rename = "rc")]
22 pub role_claim: u32,
23 #[serde(rename = "alen")]
25 pub args_length: u32,
26 #[serde(rename = "args")]
28 pub args: ByteBuf,
29}
30
31impl GspSignal {
32 pub fn bare(signal_type: u32, request_id: u32, sender_id: u32) -> Self {
34 Self {
35 signal_type,
36 request_id,
37 sender_id,
38 role_claim: 0,
39 args_length: 0,
40 args: ByteBuf::new(),
41 }
42 }
43
44 pub fn to_cbor(&self) -> Vec<u8> {
46 let mut buf = Vec::new();
47 ciborium::into_writer(self, &mut buf).expect("cbor encode");
48 buf
49 }
50
51 pub fn from_cbor(data: &[u8]) -> Result<Self, CodecError> {
53 let s: Self = ciborium::from_reader(data).map_err(|e| CodecError::Decode(e.to_string()))?;
54 if s.args_length as usize != s.args.len() {
55 return Err(CodecError::PayloadSizeMismatch);
56 }
57 Ok(s)
58 }
59
60 pub fn to_bytes(&self, codec: PayloadCodec) -> Vec<u8> {
62 match codec {
63 PayloadCodec::Cbor => self.to_cbor(),
64 PayloadCodec::Protobuf => {
65 use prost::Message as _;
66 gbp_proto::gsp::GspSignal::from(self).encode_to_vec()
67 }
68 PayloadCodec::FlatBuffers => {
69 let mut b = gbp_flat::planus::Builder::new();
70 b.finish(gbp_flat::gsp::GspSignal::from(self), None)
71 .to_vec()
72 }
73 }
74 }
75
76 pub fn from_bytes(data: &[u8], codec: PayloadCodec) -> Result<Self, CodecError> {
78 match codec {
79 PayloadCodec::Cbor => Self::from_cbor(data),
80 PayloadCodec::Protobuf => {
81 use prost::Message as _;
82 let p = gbp_proto::gsp::GspSignal::decode(data)
83 .map_err(|e| CodecError::Decode(e.to_string()))?;
84 Self::try_from(p).map_err(|_| CodecError::PayloadSizeMismatch)
85 }
86 PayloadCodec::FlatBuffers => {
87 use gbp_flat::planus::ReadAsRoot as _;
88 let r = gbp_flat::gsp::GspSignalRef::read_as_root(data)
89 .map_err(|e| CodecError::Decode(e.to_string()))?;
90 Self::try_from(r).map_err(|_| CodecError::PayloadSizeMismatch)
91 }
92 }
93 }
94}
95
96impl From<&GspSignal> for gbp_proto::gsp::GspSignal {
99 fn from(s: &GspSignal) -> Self {
100 Self {
101 signal_type: s.signal_type,
102 request_id: s.request_id,
103 sender_id: s.sender_id,
104 role_claim: s.role_claim,
105 args_length: s.args_length,
106 args: s.args.to_vec(),
107 }
108 }
109}
110
111impl TryFrom<gbp_proto::gsp::GspSignal> for GspSignal {
112 type Error = ();
113 fn try_from(p: gbp_proto::gsp::GspSignal) -> Result<Self, ()> {
114 if p.args_length as usize != p.args.len() {
115 return Err(());
116 }
117 Ok(Self {
118 signal_type: p.signal_type,
119 request_id: p.request_id,
120 sender_id: p.sender_id,
121 role_claim: p.role_claim,
122 args_length: p.args_length,
123 args: ByteBuf::from(p.args),
124 })
125 }
126}
127
128impl From<&GspSignal> for gbp_flat::gsp::GspSignal {
131 fn from(s: &GspSignal) -> Self {
132 Self {
133 signal_type: s.signal_type,
134 request_id: s.request_id,
135 sender_id: s.sender_id,
136 role_claim: s.role_claim,
137 args_length: s.args_length,
138 args: if s.args.is_empty() {
139 None
140 } else {
141 Some(s.args.to_vec())
142 },
143 }
144 }
145}
146
147impl<'a> TryFrom<gbp_flat::gsp::GspSignalRef<'a>> for GspSignal {
148 type Error = ();
149 fn try_from(r: gbp_flat::gsp::GspSignalRef<'a>) -> Result<Self, ()> {
150 let args = r.args().map_err(|_| ())?.unwrap_or(&[]).to_vec();
151 let args_length = r.args_length().map_err(|_| ())?;
152 if args_length as usize != args.len() {
153 return Err(());
154 }
155 Ok(Self {
156 signal_type: r.signal_type().map_err(|_| ())?,
157 request_id: r.request_id().map_err(|_| ())?,
158 sender_id: r.sender_id().map_err(|_| ())?,
159 role_claim: r.role_claim().map_err(|_| ())?,
160 args_length,
161 args: ByteBuf::from(args),
162 })
163 }
164}
165
166#[cfg(test)]
167mod tests {
168 use super::*;
169
170 fn sample() -> GspSignal {
171 GspSignal::bare(1, 99, 5)
172 }
173
174 #[test]
175 fn cbor_roundtrip() {
176 let orig = sample();
177 let bytes = orig.to_bytes(PayloadCodec::Cbor);
178 let decoded = GspSignal::from_bytes(&bytes, PayloadCodec::Cbor).unwrap();
179 assert_eq!(decoded.signal_type, orig.signal_type);
180 assert_eq!(decoded.request_id, orig.request_id);
181 assert_eq!(decoded.sender_id, orig.sender_id);
182 }
183
184 #[test]
185 fn protobuf_roundtrip() {
186 let orig = sample();
187 let bytes = orig.to_bytes(PayloadCodec::Protobuf);
188 let decoded = GspSignal::from_bytes(&bytes, PayloadCodec::Protobuf).unwrap();
189 assert_eq!(decoded.signal_type, orig.signal_type);
190 assert_eq!(decoded.request_id, orig.request_id);
191 assert_eq!(decoded.sender_id, orig.sender_id);
192 }
193
194 #[test]
195 fn flatbuffers_roundtrip() {
196 let orig = sample();
197 let bytes = orig.to_bytes(PayloadCodec::FlatBuffers);
198 let decoded = GspSignal::from_bytes(&bytes, PayloadCodec::FlatBuffers).unwrap();
199 assert_eq!(decoded.signal_type, orig.signal_type);
200 assert_eq!(decoded.request_id, orig.request_id);
201 assert_eq!(decoded.sender_id, orig.sender_id);
202 }
203
204 #[test]
205 fn codec_bytes_differ() {
206 let sig = sample();
207 let cbor = sig.to_bytes(PayloadCodec::Cbor);
208 let proto = sig.to_bytes(PayloadCodec::Protobuf);
209 let flat = sig.to_bytes(PayloadCodec::FlatBuffers);
210 assert_ne!(cbor, proto);
211 assert_ne!(cbor, flat);
212 assert_ne!(proto, flat);
213 }
214}