wot_td/protocol/
http.rs

1//! HTTP Binding Template
2
3use alloc::{string::String, vec::Vec};
4
5use crate::extend::ExtendableThing;
6use serde::{Deserialize, Serialize};
7use serde_with::{serde_as, skip_serializing_none};
8
9/// HTTP request method
10#[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/// HTTP Header
21#[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/// Extended fields for ExpectedResponse and AdditionalResponse
32#[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/// Extended fields for Form
44#[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/// HTTP Protocol extension
53#[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}