braid_http/types/
response.rs1use crate::protocol;
4use crate::types::{ContentRange, Version};
5use bytes::Bytes;
6use std::collections::BTreeMap;
7
8#[derive(Clone, Debug)]
10pub struct BraidResponse {
11 pub status: u16,
12 pub headers: BTreeMap<String, String>,
13 pub body: Bytes,
14 pub is_subscription: bool,
15}
16
17impl BraidResponse {
18 pub fn new(status: u16, body: impl Into<Bytes>) -> Self {
19 BraidResponse {
20 status,
21 headers: BTreeMap::new(),
22 body: body.into(),
23 is_subscription: status == 209,
24 }
25 }
26
27 pub fn subscription(body: impl Into<Bytes>) -> Self {
28 Self::new(209, body)
29 }
30
31 pub fn with_header(mut self, name: impl Into<String>, value: impl Into<String>) -> Self {
32 self.headers.insert(name.into(), value.into());
33 self
34 }
35
36 pub fn header(&self, name: &str) -> Option<&str> {
37 self.headers
38 .iter()
39 .find(|(k, _)| k.eq_ignore_ascii_case(name))
40 .map(|(_, v)| v.as_str())
41 }
42
43 pub fn get_version(&self) -> Option<Vec<Version>> {
44 self.header("version")
45 .and_then(|v| protocol::parse_version_header(v).ok())
46 }
47
48 pub fn get_parents(&self) -> Option<Vec<Version>> {
49 self.header("parents")
50 .and_then(|v| protocol::parse_version_header(v).ok())
51 }
52
53 pub fn get_current_version(&self) -> Option<Vec<Version>> {
54 self.header("current-version")
55 .and_then(|v| protocol::parse_version_header(v).ok())
56 }
57
58 pub fn get_merge_type(&self) -> Option<String> {
59 self.header("merge-type").map(|s| s.to_string())
60 }
61
62 pub fn get_content_range(&self) -> Option<ContentRange> {
63 self.header("content-range")
64 .and_then(|v| ContentRange::from_header_value(v).ok())
65 }
66
67 pub fn body_str(&self) -> Option<&str> {
68 std::str::from_utf8(&self.body).ok()
69 }
70
71 #[inline]
72 pub fn is_success(&self) -> bool {
73 (200..300).contains(&self.status)
74 }
75 #[inline]
76 pub fn is_partial(&self) -> bool {
77 self.status == 206
78 }
79}
80
81impl Default for BraidResponse {
82 fn default() -> Self {
83 BraidResponse {
84 status: 200,
85 headers: BTreeMap::new(),
86 body: Bytes::new(),
87 is_subscription: false,
88 }
89 }
90}
91
92#[cfg(feature = "fuzzing")]
93impl<'a> arbitrary::Arbitrary<'a> for BraidResponse {
94 fn arbitrary(u: &mut arbitrary::Unstructured<'a>) -> arbitrary::Result<Self> {
95 let status: u16 = u.arbitrary()?;
96 Ok(BraidResponse {
97 status,
98 headers: u.arbitrary()?,
99 body: bytes::Bytes::from(u.arbitrary::<Vec<u8>>()?),
100 is_subscription: status == 209,
101 })
102 }
103}
104
105#[cfg(test)]
106mod tests {
107 use super::*;
108
109 #[test]
110 fn test_braid_response_basic() {
111 let res = BraidResponse::new(200, "hello").with_header("Version", "\"v1\"");
112 assert_eq!(res.body_str(), Some("hello"));
113 assert_eq!(res.header("version"), Some("\"v1\""));
114 }
115}