• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

Covertness / coap-rs / 16940033320

13 Aug 2025 02:19PM UTC coverage: 81.514% (-0.8%) from 82.27%
16940033320

Pull #119

github

web-flow
Merge f07f8aec3 into 3850dc22b
Pull Request #119: Add client-side support for block-wise observe

68 of 80 new or added lines in 1 file covered. (85.0%)

6 existing lines in 3 files now uncovered.

732 of 898 relevant lines covered (81.51%)

3.54 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.65
/src/request.rs
1
use std::{net::{IpAddr, SocketAddr}, str::FromStr};
2

3
pub use coap_lite::{
4
    CoapOption, CoapRequest, MessageClass, MessageType, ObserveOption, Packet,
5
    RequestType as Method, ResponseType as Status,
6
};
7

8
/// A builder for creating CoAP requests using coap_lite
9
pub struct RequestBuilder<'a> {
10
    path: &'a str,
11
    method: Method,
12
    data: Option<Vec<u8>>,
13
    queries: Vec<Vec<u8>>,
14
    domain: String,
15
    confirmable: bool,
16
    token: Option<Vec<u8>>,
17
    options: Vec<(CoapOption, Vec<u8>)>,
18
}
19
impl<'a> RequestBuilder<'a> {
20
    pub fn new(path: &'a str, method: Method) -> Self {
2✔
21
        Self {
22
            path,
23
            method,
24
            data: None,
25
            queries: vec![],
2✔
26
            token: None,
27
            domain: "".to_string(),
4✔
28
            confirmable: true,
29
            options: vec![],
4✔
30
        }
31
    }
32

33
    /// Create a new request with the given path, method, optional payload, optional query, and
34
    /// domain.
35
    pub fn request_path(
1✔
36
        path: &'a str,
37
        method: Method,
38
        payload: Option<Vec<u8>>,
39
        query: Vec<Vec<u8>>,
40
        domain: Option<String>,
41
    ) -> Self {
42
        let new_self = Self::new(path, method);
1✔
43
        Self {
44
            data: payload,
45
            queries: query,
46
            domain: domain.unwrap_or_else(|| "".to_string()),
3✔
47
            ..new_self
48
        }
49
    }
50

51
    /// Set the payload of the request.
52
    pub fn data(mut self, data: Option<Vec<u8>>) -> Self {
1✔
53
        self.data = data;
2✔
54
        self
2✔
55
    }
56
    /// set the queries of the request.
57
    pub fn queries(mut self, queries: Vec<Vec<u8>>) -> Self {
1✔
58
        self.queries = queries;
2✔
59
        self
1✔
60
    }
61
    /// set the domain of the request.
62
    pub fn domain(mut self, domain: String) -> Self {
2✔
63
        self.domain = domain;
4✔
64
        self
2✔
65
    }
66
    /// set whether the request is confirmable.
67
    pub fn confirmable(mut self, confirmable: bool) -> Self {
1✔
68
        self.confirmable = confirmable;
1✔
69
        self
1✔
70
    }
71
    /// set the token of the request
72
    pub fn token(mut self, token: Option<Vec<u8>>) -> Self {
1✔
73
        self.token = token;
2✔
74
        self
1✔
75
    }
76
    /// set the options of the request
77
    pub fn options(mut self, options: Vec<(CoapOption, Vec<u8>)>) -> Self {
1✔
78
        self.options = options;
2✔
79
        self
1✔
80
    }
81

82
    pub fn build(self) -> CoapRequest<SocketAddr> {
1✔
83
        let mut request = CoapRequest::new();
1✔
84
        request.set_method(self.method);
1✔
85
        request.set_path(self.path);
2✔
86
        for query in self.queries {
4✔
87
            request.message.add_option(CoapOption::UriQuery, query);
2✔
88
        }
89
        for (opt, opt_data) in self.options {
7✔
90
            assert_ne!(opt, CoapOption::UriQuery, "Use queries instead");
2✔
91
            request.message.add_option(opt, opt_data);
1✔
92
        }
93
        if self.domain.len() != 0 && IpAddr::from_str(&self.domain).is_err() {
8✔
94
            request.message.add_option(
4✔
UNCOV
95
                CoapOption::UriHost,
×
96
                self.domain.as_str().as_bytes().to_vec(),
4✔
97
            );
98
        }
99
        if let Some(data) = self.data {
6✔
100
            request.message.payload = data;
1✔
101
        }
102
        match self.confirmable {
3✔
103
            true => request.message.header.set_type(MessageType::Confirmable),
6✔
104
            false => request.message.header.set_type(MessageType::NonConfirmable),
×
105
        };
106
        if let Some(tok) = self.token {
5✔
107
            request.message.set_token(tok);
2✔
108
        }
109
        return request;
4✔
110
    }
111
}
112
#[cfg(test)]
113
pub mod test {
114
    pub use super::*;
115

116
    #[test]
117
    fn test_request_has_payload() {
118
        let build = RequestBuilder::request_path(
119
            "/",
120
            Method::Put,
121
            Some(b"hello, world!".to_vec()),
122
            vec![],
123
            None,
124
        )
125
        .build();
126
        assert_eq!(build.message.payload.as_slice(), b"hello, world!");
127
    }
128
    #[test]
129
    fn test_domain() {
130
        let build = RequestBuilder::request_path(
131
            "/",
132
            Method::Put,
133
            None,
134
            vec![],
135
            Some("example.com".to_string()),
136
        )
137
        .build();
138
        assert_eq!(
139
            build.message.get_first_option(CoapOption::UriHost).unwrap(),
140
            b"example.com"
141
        );
142
    }
143

144
    #[test]
145
    fn test_domain_and_other_options() {
146
        let options = vec![
147
            (CoapOption::ProxyUri, b"coap://foo.com".to_vec()),
148
            (CoapOption::Block2, b"fake".to_vec()),
149
        ];
150
        let build = RequestBuilder::request_path(
151
            "/",
152
            Method::Put,
153
            None,
154
            vec![b"query=hello".to_vec()],
155
            Some("example.com".to_string()),
156
        )
157
        .options(options)
158
        .build();
159
        assert_eq!(
160
            build.message.get_first_option(CoapOption::UriHost).unwrap(),
161
            b"example.com"
162
        );
163
        assert_eq!(
164
            build
165
                .message
166
                .get_first_option(CoapOption::UriQuery)
167
                .unwrap(),
168
            b"query=hello"
169
        );
170
        assert_eq!(
171
            build
172
                .message
173
                .get_first_option(CoapOption::ProxyUri)
174
                .unwrap(),
175
            b"coap://foo.com"
176
        );
177
        assert_eq!(
178
            build.message.get_first_option(CoapOption::Block2).unwrap(),
179
            b"fake"
180
        )
181
    }
182

183
    #[test]
184
    fn test_request_token() {
185
        let build = RequestBuilder::new("/", Method::Put)
186
            .token(Some(b"token".to_vec()))
187
            .build();
188
        assert_eq!(build.message.get_token(), b"token");
189
    }
190
    #[test]
191
    fn test_confirmable_request() {
192
        let build = RequestBuilder::new("/", Method::Put)
193
            .confirmable(true)
194
            .build();
195
        assert_eq!(build.message.header.get_type(), MessageType::Confirmable);
196
    }
197
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc