Skip to main content

brooks_lib/dcdn/
interpret.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::{
19    fmt::{Debug, Display},
20    str::FromStr,
21};
22
23use crate::{
24    prr::{self, Prr, PrrType, ValueType},
25    utils::try_to_url,
26};
27
28/// Concrete instances of MI generated during interpretation that may be used to specify post-interpretation behavior.
29pub trait MetadataInformationResultElement: Debug {}
30
31/// TODO
32#[derive(Debug, Default)]
33pub struct MetadataInformationResultElements {
34    pub elements: Vec<Box<dyn MetadataInformationResultElement>>,
35}
36
37/// TODO
38#[derive(Debug, PartialEq)]
39pub enum Stages {
40    Pre,
41    PreClientRequest,
42    ClientRequest,
43    PostClientRequest,
44    PreOriginRequest,
45    OriginRequest,
46    PostOriginRequest,
47    Source,
48    PreOriginResponse,
49    OriginResponse,
50    PostOriginResponse,
51    PreClientResponse,
52    ClientResponse,
53    PostClientResponse,
54    Post,
55}
56
57/// The context for the interpretation of a validated representation of a given piece of Metadata Information.
58///
59///
60/// The `cookie` (of user-specified type `C`) can be used to allow access to other data
61/// an interpretation of the MI may update.
62///
63/// TODO: Determine whether the runtime can be factored out (made generic).
64#[derive(Debug)]
65pub struct InterpreterContext<C> {
66    pub request: Option<http::Request<Vec<u8>>>,
67    pub response: Option<reqwest::Response>,
68    pub runtime: tokio::runtime::Runtime,
69    pub cookie: C,
70}
71
72/// TODO
73#[derive(Debug)]
74pub enum Error {
75    InvalidType(String /* expected */, String /* actual */),
76    InvalidMetadata(Box<dyn std::error::Error>),
77    NoProcessor(String /* type for which processor is missing */),
78    InvalidInput(Box<dyn std::error::Error>),
79    AssertionFailure(Box<dyn std::error::Error>),
80    NotImplemented(String),
81    InvalidOutput,
82    RuntimeError(Box<dyn std::error::Error>),
83}
84
85impl Display for Error {
86    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
87        match self {
88            Error::NotImplemented(feature) => write!(f, "Feature {feature} is not yet implemented"),
89            Error::InvalidType(_, _) => write!(f, "Invalid type"),
90            Error::InvalidMetadata(_) => write!(f, "Invalid metadata"),
91            Error::NoProcessor(_) => write!(f, "No processor"),
92            Error::InvalidInput(e) => write!(f, "Invalid input: {e}"),
93            Error::InvalidOutput => write!(f, "Invalid output"),
94            Error::AssertionFailure(e) => write!(f, "Assertion failure: {e}"),
95            Error::RuntimeError(e) => write!(f, "Runtime error: {e}"),
96        }
97    }
98}
99
100impl std::error::Error for Error {}
101
102/// TODO
103#[derive(Debug)]
104pub struct ProcessedRequestResponse {
105    pub oheaders: http::HeaderMap<http::HeaderValue>,
106    pub obody: Vec<u8>,
107    pub ourl: url::Url,
108    pub ostatus: Option<http::StatusCode>,
109    pub omethod: http::Method,
110    pub tpe: prr::PrrType,
111}
112
113impl ProcessedRequestResponse {
114    pub fn new_request(url: &url::Url, method: &http::Method) -> Self {
115        ProcessedRequestResponse {
116            oheaders: http::HeaderMap::<http::HeaderValue>::default(),
117            obody: vec![],
118            ourl: url.clone(),
119            ostatus: None,
120            tpe: prr::PrrType::Request,
121            omethod: method.clone(),
122        }
123    }
124
125    pub fn new_response(url: &url::Url, method: &http::Method) -> Self {
126        ProcessedRequestResponse {
127            oheaders: http::HeaderMap::<http::HeaderValue>::default(),
128            obody: vec![],
129            ourl: url.clone(),
130            ostatus: None,
131            tpe: prr::PrrType::Response,
132            omethod: method.clone(),
133        }
134    }
135}
136
137impl TryFrom<&http::Request<Vec<u8>>> for ProcessedRequestResponse {
138    type Error = url::ParseError;
139
140    fn try_from(value: &http::Request<Vec<u8>>) -> Result<Self, Self::Error> {
141        let mut result =
142            ProcessedRequestResponse::new_request(&try_to_url(value.uri())?, value.method());
143
144        let mut headers = http::HeaderMap::<http::HeaderValue>::default();
145        for (k, v) in value.headers() {
146            headers.insert(k, v.clone());
147        }
148
149        result.oheaders = headers;
150        result.ostatus = None;
151        result.obody = value.body().clone();
152
153        Ok(result)
154    }
155}
156
157impl prr::Prr<Vec<u8>> for ProcessedRequestResponse {
158    fn headers(&self) -> Vec<(String, http::HeaderValue)> {
159        self.oheaders
160            .iter()
161            .map(|(k, v)| (k.to_string(), v.clone()))
162            .collect()
163    }
164
165    fn set_header_value(&mut self, header: &str, value: &str) -> prr::Result<()> {
166        let header_to_set = http::HeaderName::from_str(header).map_err(|_| {
167            prr::Error::BadValue(prr::Value {
168                tpe: prr::ValueType::HeaderName,
169                value: Some(header.to_string()),
170            })
171        })?;
172        let header_to_set_value = http::HeaderValue::from_str(value).map_err(|_| {
173            prr::Error::BadValue(prr::Value {
174                tpe: prr::ValueType::HeaderValue,
175                value: Some(value.to_string()),
176            })
177        })?;
178
179        self.oheaders.insert(header_to_set, header_to_set_value);
180        Ok(())
181    }
182
183    fn remove_header(&mut self, header: &str) -> prr::Result<()> {
184        let header_to_remove = http::HeaderName::from_str(header).map_err(|_| {
185            prr::Error::BadValue(prr::Value {
186                tpe: prr::ValueType::HeaderName,
187                value: Some(header.to_string()),
188            })
189        })?;
190        self.oheaders.remove(header_to_remove);
191        Ok(())
192    }
193
194    fn add_header(&mut self, header: &str, value: &str) -> prr::Result<()> {
195        self.set_header_value(header, value)
196    }
197
198    fn url(&self) -> prr::Result<url::Url> {
199        Ok(self.ourl.clone())
200    }
201
202    fn set_url(&mut self, url: &url::Url) -> prr::Result<()> {
203        self.ourl = url.clone();
204        Ok(())
205    }
206
207    fn set_status(&mut self, response: &u16) -> prr::Result<()> {
208        self.ostatus = Some(http::StatusCode::from_u16(*response).map_err(|_| {
209            prr::Error::BadValue(prr::Value {
210                tpe: prr::ValueType::Status,
211                value: Some(response.to_string()),
212            })
213        })?);
214        Ok(())
215    }
216
217    fn clear_headers(&mut self) -> prr::Result<()> {
218        self.oheaders.clear();
219        Ok(())
220    }
221
222    fn set_body(&mut self, body: &Vec<u8>) -> prr::Result<()> {
223        self.obody = body.clone();
224        Ok(())
225    }
226
227    fn get_body(&self) -> prr::Result<&Vec<u8>> {
228        Ok(&self.obody)
229    }
230
231    fn get_status(&self) -> prr::Result<http::StatusCode> {
232        self.ostatus
233            .ok_or(prr::Error::MissingValue(prr::ValueType::Status))
234    }
235
236    fn tpe(&self) -> prr::PrrType {
237        self.tpe.clone()
238    }
239
240    fn set_method(&mut self, method: &http::Method) -> prr::Result<()> {
241        self.omethod = method.clone();
242        Ok(())
243    }
244
245    fn get_method(&self) -> http::Method {
246        self.omethod.clone()
247    }
248}
249
250impl TryFrom<&dyn Prr<Vec<u8>>> for reqwest::Request {
251    type Error = prr::Error;
252
253    fn try_from(value: &dyn Prr<Vec<u8>>) -> std::result::Result<Self, Self::Error> {
254        if value.tpe() != PrrType::Request {
255            return Err(prr::Error::InvalidMode);
256        }
257
258        let url = value
259            .url()
260            .map_err(|_| prr::Error::MissingValue(ValueType::Url))?;
261
262        let mut request = reqwest::Request::new(reqwest::Method::GET, url);
263
264        // Put in the headers.
265        for (header_key, header_value) in value.headers() {
266            request.headers_mut().insert(
267                http::HeaderName::from_str(&header_key).map_err(|_| {
268                    prr::Error::BadValue(prr::Value {
269                        tpe: ValueType::HeaderName,
270                        value: Some(header_key.clone()),
271                    })
272                })?,
273                header_value,
274            );
275        }
276
277        // Configure the body.
278        request.body_mut().replace(
279            value
280                .get_body()
281                .map_err(|_| prr::Error::MissingValue(ValueType::Body))?
282                .clone()
283                .into(),
284        );
285
286        Ok(request)
287    }
288}