1use alloc::{string::String, vec::Vec};
4
5use crate::extend::ExtendableThing;
6use serde::{Deserialize, Serialize};
7use serde_with::{serde_as, skip_serializing_none};
8
9#[derive(Debug, Clone, Copy, Deserialize, Serialize, PartialEq, Eq, Hash)]
11#[serde(rename_all = "SCREAMING_SNAKE_CASE")]
12pub enum Method {
13 Get,
14 Put,
15 Post,
16 Delete,
17 Patch,
18}
19
20#[serde_as]
22#[skip_serializing_none]
23#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, Default)]
24pub struct MessageHeader {
25 #[serde(rename = "htv:fieldName")]
26 pub field_name: Option<String>,
27 #[serde(rename = "htv:fieldValue")]
28 pub field_value: Option<String>,
29}
30
31#[serde_as]
33#[skip_serializing_none]
34#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, Hash)]
35pub struct Response {
36 #[serde(rename = "htv:headers")]
37 #[serde(skip_serializing_if = "Vec::is_empty")]
38 pub headers: Vec<MessageHeader>,
39 #[serde(rename = "htv:statusCodeValue")]
40 pub status_code_value: Option<usize>,
41}
42
43#[serde_as]
45#[skip_serializing_none]
46#[derive(Debug, Clone, Deserialize, Serialize, Default, PartialEq, Eq, Hash)]
47pub struct Form {
48 #[serde(rename = "htv:methodName")]
49 pub method_name: Option<Method>,
50}
51
52#[derive(Debug, Clone, Deserialize, Serialize, PartialEq, Eq, Hash, Default)]
54pub struct HttpProtocol {}
55
56impl ExtendableThing for HttpProtocol {
57 type InteractionAffordance = ();
58 type PropertyAffordance = ();
59 type ActionAffordance = ();
60 type EventAffordance = ();
61 type Form = Form;
62 type ExpectedResponse = Response;
63 type DataSchema = ();
64 type ObjectSchema = ();
65 type ArraySchema = ();
66}
67
68#[cfg(test)]
69mod test {
70 use alloc::vec;
71
72 use super::HttpProtocol;
73 use crate::thing::{ExpectedResponse, Form};
74
75 fn deserialize_form(s: &str, r: Form<HttpProtocol>) {
76 let f: Form<HttpProtocol> = serde_json::from_str(s).unwrap();
77 assert_eq!(f, r);
78 }
79
80 #[test]
81 fn deserialize_discovery_property() {
82 let property = r#"
83 {
84 "href": "/things{?offset,limit,format,sort_by,sort_order}",
85 "htv:methodName": "GET",
86 "response": {
87 "description": "Success response",
88 "htv:statusCodeValue": 200,
89 "contentType": "application/ld+json",
90 "htv:headers": [
91 {
92 "htv:fieldName": "Link"
93 }
94 ]
95 }
96 }
97 "#;
98
99 let expected = Form {
100 href: "/things{?offset,limit,format,sort_by,sort_order}".into(),
101 response: Some(ExpectedResponse {
102 content_type: "application/ld+json".into(),
103 other: super::Response {
104 headers: vec![super::MessageHeader {
105 field_name: Some("Link".into()),
106 field_value: None,
107 }],
108 status_code_value: Some(200),
109 },
110 }),
111 other: super::Form {
112 method_name: Some(super::Method::Get),
113 },
114 ..Default::default()
115 };
116
117 deserialize_form(property, expected);
118 }
119
120 #[test]
121 fn deserialize_discovery_action() {
122 let action = r#"
123 {
124 "href": "/things",
125 "htv:methodName": "POST",
126 "response": {
127 "contentType": "application/td+json",
128 "description": "Success response including the system-generated URI",
129 "htv:headers": [
130 {
131 "description": "System-generated URI",
132 "htv:fieldName": "Location"
133 }
134 ],
135 "htv:statusCodeValue": 201
136 }
137 }
138 "#;
139
140 let expected = Form {
141 op: Default::default(),
142 href: "/things".into(),
143 response: Some(ExpectedResponse {
144 content_type: "application/td+json".into(),
145 other: super::Response {
146 headers: vec![super::MessageHeader {
147 field_name: Some("Location".into()),
148 field_value: None,
149 }],
150 status_code_value: Some(201),
151 },
152 }),
153 other: super::Form {
154 method_name: Some(super::Method::Post),
155 },
156 ..Default::default()
157 };
158
159 deserialize_form(action, expected);
160 }
161}