use pyo3::prelude::*;
use pyo3::types::PyBytes;
use crate::response::Response;
#[pyclass(name = "Response")]
pub struct PyResponse {
status: u16,
headers: Vec<(String, String)>,
body: Vec<u8>,
}
impl From<Response> for PyResponse {
fn from(resp: Response) -> Self {
let headers = resp
.headers()
.iter()
.filter_map(|(k, v)| {
v.to_str()
.ok()
.map(|v| (k.as_str().to_string(), v.to_string()))
})
.collect();
Self {
status: resp.status_code(),
headers,
body: resp.into_bytes().to_vec(),
}
}
}
#[pymethods]
impl PyResponse {
#[getter]
fn status_code(&self) -> u16 {
self.status
}
#[getter]
fn ok(&self) -> bool {
(200..300).contains(&self.status)
}
#[getter]
fn is_redirect(&self) -> bool {
(300..400).contains(&self.status)
}
#[getter]
fn headers(&self) -> Vec<(String, String)> {
self.headers.clone()
}
fn header(&self, name: &str) -> Option<String> {
let name_lower = name.to_lowercase();
self.headers
.iter()
.find(|(k, _)| k.to_lowercase() == name_lower)
.map(|(_, v)| v.clone())
}
#[getter]
fn content_type(&self) -> Option<String> {
self.header("content-type")
}
fn text(&self) -> PyResult<String> {
String::from_utf8(self.body.clone())
.map_err(|e| pyo3::exceptions::PyUnicodeDecodeError::new_err(e.to_string()))
}
fn content<'py>(&self, py: Python<'py>) -> Bound<'py, PyBytes> {
PyBytes::new(py, &self.body)
}
fn __len__(&self) -> usize {
self.body.len()
}
fn __repr__(&self) -> String {
format!("<Response [{}]>", self.status)
}
fn json<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
let text = self.text()?;
let json_module = py.import("json")?;
json_module.call_method1("loads", (text,))
}
}