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 http::Uri {
41    fn into_url(self) -> Result<Url> {
42        Url::parse(&self.to_string()).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
52/// Request body variants.
53#[derive(Clone, Debug, Default)]
54pub enum Body {
55    #[default]
56    Empty,
57    Bytes(Bytes),
58    Text(String),
59    Json(Vec<u8>),
60    Form(String),
61    Raw(Vec<u8>),
62}
63
64impl Body {
65    pub fn empty() -> Self {
66        Body::Empty
67    }
68
69    pub fn is_empty(&self) -> bool {
70        matches!(self, Body::Empty)
71    }
72
73    pub fn into_bytes(self) -> Result<Bytes> {
74        Ok(match self {
75            Body::Empty => Bytes::new(),
76            Body::Bytes(bytes) => bytes,
77            Body::Text(text) => Bytes::from(text.into_bytes()),
78            Body::Json(bytes) => Bytes::from(bytes),
79            Body::Form(text) => Bytes::from(text.into_bytes()),
80            Body::Raw(bytes) => Bytes::from(bytes),
81        })
82    }
83}
84
85impl From<Bytes> for Body {
86    fn from(value: Bytes) -> Self {
87        Body::Bytes(value)
88    }
89}
90
91impl From<Vec<u8>> for Body {
92    fn from(value: Vec<u8>) -> Self {
93        Body::Raw(value)
94    }
95}
96
97impl From<&[u8]> for Body {
98    fn from(value: &[u8]) -> Self {
99        Body::Raw(value.to_vec())
100    }
101}
102
103impl From<String> for Body {
104    fn from(value: String) -> Self {
105        Body::Text(value)
106    }
107}
108
109impl From<&str> for Body {
110    fn from(value: &str) -> Self {
111        Body::Text(value.to_string())
112    }
113}
114
115/// High-level request object for execution.
116#[derive(Clone, Debug)]
117pub struct Request {
118    pub(crate) method: Method,
119    pub(crate) url: Url,
120    pub(crate) headers: Headers,
121    pub(crate) body: Body,
122    pub(crate) version: Option<HttpVersion>,
123    pub(crate) timeout: Option<Duration>,
124}
125
126impl Request {
127    pub fn new(method: Method, url: Url) -> Self {
128        Self {
129            method,
130            url,
131            headers: Headers::new(),
132            body: Body::Empty,
133            version: None,
134            timeout: None,
135        }
136    }
137
138    pub fn method(&self) -> &Method {
139        &self.method
140    }
141
142    pub fn url(&self) -> &Url {
143        &self.url
144    }
145
146    pub fn headers(&self) -> &Headers {
147        &self.headers
148    }
149
150    pub fn body(&self) -> &Body {
151        &self.body
152    }
153
154    pub fn version(&self) -> Option<HttpVersion> {
155        self.version
156    }
157
158    pub fn timeout(&self) -> Option<Duration> {
159        self.timeout
160    }
161}
162
163/// Redirect policy for the client.
164#[derive(Clone, Debug, Default)]
165pub enum RedirectPolicy {
166    #[default]
167    None,
168    Limited(u32),
169}