Skip to main content

musdk_common/function/
response.rs

1use std::{borrow::Cow, collections::HashMap};
2
3use borsh::{BorshDeserialize, BorshSerialize};
4
5use crate::http_client::{
6    header::{BINARY_CONTENT_TYPE, CONTENT_TYPE_HEADER, STRING_CONTENT_TYPE},
7    Header, Status,
8};
9
10#[derive(Debug, BorshSerialize, BorshDeserialize)]
11pub struct Response<'a> {
12    pub status: Status,
13    pub headers: Vec<Header<'a>>,
14    pub body: Cow<'a, [u8]>,
15}
16
17impl<'a> Response<'a> {
18    /// Create a [`ResponseBuilder`]
19    pub fn builder() -> ResponseBuilder<'a> {
20        ResponseBuilder::default()
21    }
22}
23
24pub struct ResponseBuilder<'a> {
25    status: Status,
26    headers: HashMap<Cow<'a, str>, Header<'a>>,
27}
28
29impl<'a> ResponseBuilder<'a> {
30    pub fn new() -> Self {
31        ResponseBuilder {
32            status: Status::Ok,
33            headers: HashMap::new(),
34        }
35    }
36
37    pub fn status(mut self, status: Status) -> Self {
38        self.status = status;
39        self
40    }
41
42    pub fn content_type(mut self, content_type: Cow<'a, str>) -> Self {
43        let header = Header {
44            name: Cow::Borrowed(CONTENT_TYPE_HEADER),
45            value: content_type,
46        };
47
48        self.headers.remove(&header.name);
49        self.headers.insert(header.name.clone(), header);
50        self
51    }
52
53    fn has_content_type(&self) -> bool {
54        self.headers
55            .contains_key(&Cow::Borrowed(CONTENT_TYPE_HEADER))
56    }
57
58    /// Adds a [`Header`] to response and overrides the header if already exists.
59    pub fn header(mut self, header: Header<'a>) -> Self {
60        let name: Cow<'a, str> = header.name.to_lowercase().into();
61        self.headers.remove(&name);
62        self.headers.insert(name, header);
63        self
64    }
65
66    pub fn headers(self, headers: Vec<Header<'a>>) -> Self {
67        headers.into_iter().fold(self, Self::header)
68    }
69
70    pub fn no_body(self) -> Response<'a> {
71        Response {
72            status: self.status,
73            headers: self.headers.into_values().collect(),
74            body: Cow::Borrowed(&[]),
75        }
76    }
77
78    pub fn body_from_slice(mut self, slice: &'a [u8]) -> Response<'a> {
79        if !self.has_content_type() {
80            self = self.content_type(Cow::Borrowed(BINARY_CONTENT_TYPE));
81        }
82
83        Response {
84            status: self.status,
85            headers: self.headers.into_values().collect(),
86            body: Cow::Borrowed(slice),
87        }
88    }
89
90    pub fn body_from_vec(mut self, vec: Vec<u8>) -> Response<'a> {
91        if !self.has_content_type() {
92            self = self.content_type(Cow::Borrowed(BINARY_CONTENT_TYPE));
93        }
94
95        Response {
96            status: self.status,
97            headers: self.headers.into_values().collect(),
98            body: Cow::Owned(vec),
99        }
100    }
101
102    pub fn body_from_string(mut self, string: String) -> Response<'a> {
103        if !self.has_content_type() {
104            self = self.content_type(Cow::Borrowed(STRING_CONTENT_TYPE));
105        }
106
107        Response {
108            status: self.status,
109            headers: self.headers.into_values().collect(),
110            body: Cow::Owned(string.as_bytes().to_vec()),
111        }
112    }
113
114    pub fn body_from_str(mut self, str: &'a str) -> Response<'a> {
115        if !self.has_content_type() {
116            self = self.content_type(Cow::Borrowed(STRING_CONTENT_TYPE));
117        }
118
119        Response {
120            status: self.status,
121            headers: self.headers.into_values().collect(),
122            body: Cow::Borrowed(str.as_bytes()),
123        }
124    }
125}
126
127impl<'a> Default for ResponseBuilder<'a> {
128    fn default() -> Self {
129        Self::new()
130    }
131}