Skip to main content

brooks_lib/
prr.rs

1// brooks, Copyright 2026, Will Hawkins
2//
3// This file is part of brooks.
4
5// This file is free software: you can redistribute it and/or modify
6// it under the terms of the GNU General Public License as published by
7// the Free Software Foundation, either version 3 of the License, or
8// (at your option) any later version.
9//
10// This program is distributed in the hope that it will be useful,
11// but WITHOUT ANY WARRANTY; without even the implied warranty of
12// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13// GNU General Public License for more details.
14//
15// You should have received a copy of the GNU General Public License
16// along with this program. If not, see <https://www.gnu.org/licenses/>.
17
18use std::error;
19use std::fmt::{Debug, Display};
20
21use http::{HeaderValue, Method, StatusCode};
22
23use crate::utils::try_to_uri;
24
25#[derive(Debug, Clone)]
26pub enum ValueType {
27    Url,
28    HeaderName,
29    HeaderValue,
30    Status,
31    Body,
32}
33
34impl Display for ValueType {
35    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
36        match self {
37            ValueType::Url => write!(f, "Url"),
38            ValueType::HeaderName => write!(f, "Header name"),
39            ValueType::HeaderValue => write!(f, "Header value"),
40            ValueType::Body => write!(f, "Body"),
41            ValueType::Status => write!(f, "Status"),
42        }
43    }
44}
45
46#[derive(Debug, Clone)]
47pub struct Value {
48    pub tpe: ValueType,
49    pub value: Option<String>,
50}
51
52impl Display for Value {
53    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
54        if let Some(value) = self.value.as_ref() {
55            write!(f, "{}: {}", self.tpe, value)
56        } else {
57            write!(f, "{}", self.tpe)
58        }
59    }
60}
61
62#[derive(Debug, Clone, PartialEq)]
63pub enum PrrType {
64    Request,
65    Response,
66}
67
68#[derive(Debug, Clone)]
69pub enum Error {
70    BadValue(Value),
71    MissingValue(ValueType),
72    BadStatus,
73    InvalidMode,
74}
75
76impl error::Error for Error {}
77
78impl Display for Error {
79    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
80        match &self {
81            Error::BadValue(v) => write!(f, "Bad value: {v}"),
82            Error::BadStatus => write!(f, "Bad Status"),
83            Error::InvalidMode => write!(f, "Invalid mode"),
84            Error::MissingValue(processable_request_response_value_type) => write!(
85                f,
86                "Missing value: {processable_request_response_value_type}"
87            ),
88        }
89    }
90}
91
92pub type Result<T> = std::result::Result<T, Error>;
93
94/// A request or response that can be manipulated during processing of metadata.
95pub trait Prr<Body>: Debug {
96    // Header Manipulation and Access
97
98    fn headers(&self) -> Vec<(String, HeaderValue)>;
99    fn set_header_value(&mut self, header: &str, value: &str) -> Result<()>;
100    fn clear_headers(&mut self) -> Result<()>;
101    fn remove_header(&mut self, header: &str) -> Result<()>;
102    fn add_header(&mut self, header: &str, value: &str) -> Result<()>;
103
104    // URI Manipulation and Access
105
106    fn url(&self) -> Result<url::Url>;
107    fn set_url(&mut self, url: &url::Url) -> Result<()>;
108
109    // Status Manipulation and Access
110
111    fn set_status(&mut self, response: &u16) -> Result<()>;
112    fn get_status(&self) -> Result<StatusCode>;
113
114    // Body Manipulation and Access
115
116    fn set_body(&mut self, body: &Body) -> Result<()>;
117    fn get_body(&self) -> Result<&Body>;
118
119    // Method Manipulation and Access
120
121    fn set_method(&mut self, method: &Method) -> Result<()>;
122    fn get_method(&self) -> Method;
123
124    /// Get the type of processable request/response.
125    fn tpe(&self) -> PrrType;
126}
127
128impl TryFrom<&dyn Prr<Vec<u8>>> for http::Request<Vec<u8>> {
129    type Error = Error;
130
131    fn try_from(value: &dyn Prr<Vec<u8>>) -> std::result::Result<Self, Self::Error> {
132        if value.tpe() != PrrType::Request {
133            return Err(Error::InvalidMode);
134        }
135
136        let mut request_builder = http::request::Builder::new();
137
138        // Put in the headers.
139        for (header_key, header_value) in value.headers() {
140            request_builder = request_builder.header(header_key, header_value);
141        }
142
143        let url = value
144            .url()
145            .map_err(|_| Error::MissingValue(ValueType::Url))?;
146
147        // Set the URI.
148        request_builder = request_builder.uri(try_to_uri(&url).map_err(|_| {
149            Error::BadValue(Value {
150                tpe: ValueType::Url,
151                value: Some(url.to_string()),
152            })
153        })?);
154
155        // Configure the body.
156        request_builder
157            .body(
158                value
159                    .get_body()
160                    .map_err(|_| Error::MissingValue(ValueType::Body))?
161                    .clone(),
162            )
163            .map_err(|_| {
164                Error::BadValue(Value {
165                    tpe: ValueType::Body,
166                    value: None,
167                })
168            })
169    }
170}
171impl TryFrom<&dyn Prr<Vec<u8>>> for http::Response<Vec<u8>> {
172    type Error = Error;
173
174    fn try_from(value: &dyn Prr<Vec<u8>>) -> std::result::Result<Self, Self::Error> {
175        if value.tpe() != PrrType::Response {
176            return Err(Error::InvalidMode);
177        }
178
179        let mut response_builder = http::response::Builder::new();
180
181        // Put in the headers.
182        for (header_key, header_value) in value.headers() {
183            response_builder = response_builder.header(header_key, header_value);
184        }
185
186        // Set the status.
187        response_builder =
188            response_builder.status(value.get_status().map_err(|_| Error::BadStatus)?);
189
190        // Configure the body.
191        response_builder
192            .body(
193                value
194                    .get_body()
195                    .map_err(|_| Error::MissingValue(ValueType::Body))?
196                    .clone(),
197            )
198            .map_err(|_| {
199                Error::BadValue(Value {
200                    tpe: ValueType::Body,
201                    value: None,
202                })
203            })
204    }
205}