1use crate::extend::ExtendableThing;
4use serde::{Deserialize, Serialize};
5use serde_repr::{Deserialize_repr, Serialize_repr};
6use serde_with::{serde_as, skip_serializing_none};
7
8#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)]
10#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
11pub enum Method {
12 Get,
13 Put,
14 Post,
15 Delete,
16 Patch,
17 Fetch,
18 #[serde(rename = "iPATCH")]
19 Ipatch,
20}
21
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Deserialize_repr, Serialize_repr)]
24#[repr(u16)]
25pub enum BlockSize {
26 Size16 = 16,
27 Size32 = 32,
28 Size64 = 64,
29 Size128 = 128,
30 Size256 = 256,
31 Size512 = 512,
32 Size1024 = 1024,
33}
34
35#[serde_as]
43#[skip_serializing_none]
44#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash, Default)]
45pub struct BlockWiseTransferParameters {
46 #[serde(rename = "cov:block2Size")]
47 pub block2_size: Option<BlockSize>,
48 #[serde(rename = "cov:block1Size")]
49 pub block1_size: Option<BlockSize>,
50}
51
52#[serde_as]
54#[skip_serializing_none]
55#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, Default)]
56pub struct Form {
57 #[serde(rename = "cov:method")]
58 pub method: Option<Method>,
59 #[serde(rename = "cov:blockwise")]
60 pub blockwise: Option<BlockWiseTransferParameters>,
61 #[serde(rename = "cov:qblockwise")]
62 pub qblockwise: Option<BlockWiseTransferParameters>,
63 #[serde(rename = "cov:hopLimit")]
64 pub hop_limit: Option<u8>,
65 #[serde(rename = "cov:accept")]
66 pub accept: Option<u16>,
67 #[serde(rename = "cov:contentFormat")]
68 pub content_format: Option<u16>,
69}
70
71#[serde_as]
73#[skip_serializing_none]
74#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, Default)]
75pub struct ExpectedResponse {
76 #[serde(rename = "cov:contentFormat")]
77 pub content_format: Option<u16>,
78}
79
80#[derive(Debug, Clone, Copy, Serialize, Deserialize, PartialEq, Eq, Hash, Default)]
82pub struct CoapProtocol {}
83
84impl ExtendableThing for CoapProtocol {
85 type InteractionAffordance = ();
86 type PropertyAffordance = ();
87 type ActionAffordance = ();
88 type EventAffordance = ();
89 type Form = Form;
90 type ExpectedResponse = ExpectedResponse;
91 type DataSchema = ();
92 type ObjectSchema = ();
93 type ArraySchema = ();
94}
95
96#[cfg(test)]
97mod test {
98 use alloc::vec;
99
100 use super::{BlockSize, CoapProtocol};
101 use crate::thing::{ExpectedResponse, Form};
102 fn deserialize_form(s: &str, r: Form<CoapProtocol>) {
103 let f: crate::thing::Form<CoapProtocol> = serde_json::from_str(s).unwrap();
104
105 assert_eq!(f, r);
106 }
107
108 #[test]
109 fn deserialize_observe() {
110 let form = r#"
111 {
112 "cov:method": "GET",
113 "href": "coap://[2001:DB8::1]/status",
114 "contentType": "text/plain;charset=utf-8",
115 "subprotocol": "cov:observe",
116 "op": ["observeproperty"]
117 }
118 "#;
119 let expected = Form {
120 op: crate::thing::DefaultedFormOperations::Custom(vec![
121 crate::thing::FormOperation::ObserveProperty,
122 ]),
123 href: "coap://[2001:DB8::1]/status".into(),
124 content_type: Some("text/plain;charset=utf-8".into()),
125 subprotocol: Some("cov:observe".into()),
126 other: super::Form {
127 method: Some(super::Method::Get),
128 ..Default::default()
129 },
130 ..Default::default()
131 };
132
133 deserialize_form(form, expected);
134 }
135
136 #[test]
137 fn deserialize_blockwise() {
138 let form = r#"
139 {
140 "href": "coap://[2001:DB8::1]/status",
141 "contentType": "text/plain;charset=utf-8",
142 "cov:blockwise": { }
143 }
144 "#;
145 let expected = Form {
146 href: "coap://[2001:DB8::1]/status".into(),
147 content_type: Some("text/plain;charset=utf-8".into()),
148 other: super::Form {
149 blockwise: Some(super::BlockWiseTransferParameters::default()),
150 ..Default::default()
151 },
152 ..Default::default()
153 };
154
155 deserialize_form(form, expected);
156 }
157
158 #[test]
159 fn deserialize_qblockwise_params() {
160 let form = r#"
161 {
162 "href": "coap://[2001:DB8::1]/status",
163 "contentType": "text/plain;charset=utf-8",
164 "cov:qblockwise": {
165 "cov:block2Size": 64
166 }
167 }
168 "#;
169 let expected = Form {
170 href: "coap://[2001:DB8::1]/status".into(),
171 content_type: Some("text/plain;charset=utf-8".into()),
172 other: super::Form {
173 qblockwise: Some(super::BlockWiseTransferParameters {
174 block2_size: Some(BlockSize::Size64),
175 ..Default::default()
176 }),
177 ..Default::default()
178 },
179 ..Default::default()
180 };
181
182 deserialize_form(form, expected);
183 }
184
185 #[test]
186 fn deserialize_hop_limit() {
187 let form = r#"
188 {
189 "href": "coap://[2001:DB8::1]/status",
190 "contentType": "text/plain;charset=utf-8",
191 "cov:hopLimit": 5
192 }
193 "#;
194 let expected = Form {
195 href: "coap://[2001:DB8::1]/status".into(),
196 content_type: Some("text/plain;charset=utf-8".into()),
197 other: super::Form {
198 hop_limit: Some(5),
199 ..Default::default()
200 },
201 ..Default::default()
202 };
203
204 deserialize_form(form, expected);
205 }
206
207 #[test]
208 fn deserialize_content_format() {
209 let form = r#"
210 {
211 "href": "coap://[2001:DB8::1]/status",
212 "contentType": "application/cbor",
213 "cov:contentFormat": 60,
214 "cov:accept": 60,
215 "response": {
216 "contentType": "application/cbor",
217 "cov:contentFormat": 60
218 }
219 }
220 "#;
221 let expected = Form {
222 href: "coap://[2001:DB8::1]/status".into(),
223 content_type: Some("application/cbor".into()),
224 other: super::Form {
225 content_format: Some(60),
226 accept: Some(60),
227 ..Default::default()
228 },
229 response: Some(ExpectedResponse {
230 content_type: "application/cbor".into(),
231 other: super::ExpectedResponse {
232 content_format: Some(60),
233 },
234 }),
235 ..Default::default()
236 };
237
238 deserialize_form(form, expected);
239 }
240}