Skip to main content

specter/
request.rs

1//! Request and body types with reqwest-like ergonomics.
2
3use crate::error::{Error, Result};
4use crate::headers::Headers;
5use crate::version::HttpVersion;
6use bytes::Bytes;
7use http::Method;
8use std::time::Duration;
9use url::Url;
10
11/// Convert common URL inputs into a `Url`.
12pub trait IntoUrl {
13    fn into_url(self) -> Result<Url>;
14}
15
16impl IntoUrl for Url {
17    fn into_url(self) -> Result<Url> {
18        Ok(self)
19    }
20}
21
22impl IntoUrl for &Url {
23    fn into_url(self) -> Result<Url> {
24        Ok(self.clone())
25    }
26}
27
28impl IntoUrl for &str {
29    fn into_url(self) -> Result<Url> {
30        Url::parse(self).map_err(Error::from)
31    }
32}
33
34impl IntoUrl for String {
35    fn into_url(self) -> Result<Url> {
36        Url::parse(&self).map_err(Error::from)
37    }
38}
39
40impl IntoUrl for &String {
41    fn into_url(self) -> Result<Url> {
42        Url::parse(self).map_err(Error::from)
43    }
44}
45
46impl IntoUrl for http::Uri {
47    fn into_url(self) -> Result<Url> {
48        Url::parse(&self.to_string()).map_err(Error::from)
49    }
50}
51
52impl IntoUrl for &http::Uri {
53    fn into_url(self) -> Result<Url> {
54        Url::parse(&self.to_string()).map_err(Error::from)
55    }
56}
57
58/// Request body variants.
59#[derive(Clone, Debug, Default)]
60pub enum Body {
61    #[default]
62    Empty,
63    Bytes(Bytes),
64    Text(String),
65    Json(Vec<u8>),
66    Form(String),
67    Raw(Vec<u8>),
68}
69
70impl Body {
71    pub fn empty() -> Self {
72        Body::Empty
73    }
74
75    pub fn is_empty(&self) -> bool {
76        matches!(self, Body::Empty)
77    }
78
79    pub fn into_bytes(self) -> Result<Bytes> {
80        Ok(match self {
81            Body::Empty => Bytes::new(),
82            Body::Bytes(bytes) => bytes,
83            Body::Text(text) => Bytes::from(text.into_bytes()),
84            Body::Json(bytes) => Bytes::from(bytes),
85            Body::Form(text) => Bytes::from(text.into_bytes()),
86            Body::Raw(bytes) => Bytes::from(bytes),
87        })
88    }
89}
90
91impl From<Bytes> for Body {
92    fn from(value: Bytes) -> Self {
93        Body::Bytes(value)
94    }
95}
96
97impl From<Vec<u8>> for Body {
98    fn from(value: Vec<u8>) -> Self {
99        Body::Raw(value)
100    }
101}
102
103impl From<&[u8]> for Body {
104    fn from(value: &[u8]) -> Self {
105        Body::Raw(value.to_vec())
106    }
107}
108
109impl From<String> for Body {
110    fn from(value: String) -> Self {
111        Body::Text(value)
112    }
113}
114
115impl From<&str> for Body {
116    fn from(value: &str) -> Self {
117        Body::Text(value.to_string())
118    }
119}
120
121/// High-level request object for execution.
122#[derive(Clone, Debug)]
123pub struct Request {
124    pub(crate) method: Method,
125    pub(crate) url: Url,
126    pub(crate) headers: Headers,
127    pub(crate) body: Body,
128    pub(crate) version: Option<HttpVersion>,
129    pub(crate) timeout: Option<Duration>,
130}
131
132impl Request {
133    pub fn new(method: Method, url: Url) -> Self {
134        Self {
135            method,
136            url,
137            headers: Headers::new(),
138            body: Body::Empty,
139            version: None,
140            timeout: None,
141        }
142    }
143
144    pub fn method(&self) -> &Method {
145        &self.method
146    }
147
148    pub fn url(&self) -> &Url {
149        &self.url
150    }
151
152    pub fn headers(&self) -> &Headers {
153        &self.headers
154    }
155
156    pub fn body(&self) -> &Body {
157        &self.body
158    }
159
160    pub fn version(&self) -> Option<HttpVersion> {
161        self.version
162    }
163
164    pub fn timeout(&self) -> Option<Duration> {
165        self.timeout
166    }
167}
168
169/// Redirect policy for the client.
170#[derive(Clone, Debug, Default)]
171pub enum RedirectPolicy {
172    #[default]
173    None,
174    Limited(u32),
175}