use pyo3::exceptions::PyException;
use pyo3::prelude::*;
use pyo3::types::{PyDict, PyList, PyString};
type ClientError = example_client::error::Error;
pub type PyExampleResult<T> = ::core::result::Result<T, PyExampleError>;
pub struct ApiErrorParts {
pub message: String,
pub code: Option<String>,
pub status: u16,
pub details: ::std::vec::Vec<::serde_json::Value>,
}
pub trait ApiErrorAnswer: Sized {
fn into_api_error(self) -> ::core::result::Result<ApiErrorParts, Self>;
}
impl ApiErrorAnswer for ClientError {
fn into_api_error(self) -> ::core::result::Result<ApiErrorParts, Self> {
match self {
ClientError::Api(api) => {
Ok(ApiErrorParts {
message: api.message,
code: api.code,
status: api.status,
details: api.details,
})
}
other => Err(other),
}
}
}
#[derive(Debug)]
pub enum PyExampleError {
Client(ClientError),
Python(PyErr),
}
impl ::core::convert::From<ClientError> for PyExampleError {
fn from(e: ClientError) -> Self {
PyExampleError::Client(e)
}
}
impl ::core::convert::From<PyErr> for PyExampleError {
fn from(e: PyErr) -> Self {
PyExampleError::Python(e)
}
}
impl ::core::convert::From<::url::ParseError> for PyExampleError {
fn from(e: ::url::ParseError) -> Self {
PyExampleError::Client(ClientError::from(e))
}
}
impl ::core::convert::From<PyExampleError> for PyErr {
fn from(e: PyExampleError) -> Self {
match e {
PyExampleError::Python(e) => e,
PyExampleError::Client(e) => {
Python::attach(|py| client_exception(py, e).unwrap_or_else(|err| err))
}
}
}
}
#[::pyo3::pyclass(
extends = PyException,
subclass,
frozen,
name = "ExampleError",
module = "example._core",
)]
pub struct ExampleError {
message: String,
code: Option<String>,
status: Option<u16>,
details: Py<PyList>,
}
#[::pyo3::pymethods]
impl ExampleError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> Self {
Self {
message,
code,
status,
details: details.map_or_else(|| PyList::empty(py).unbind(), Bound::unbind),
}
}
#[pyo3(signature = (message, code = None, status = None, details = None))]
#[allow(unused_variables)]
fn __init__(
slf: &Bound<'_, Self>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyResult<()> {
slf.setattr("args", (message,))
}
#[getter]
fn message(&self) -> &str {
&self.message
}
#[getter]
fn code(&self) -> Option<&str> {
self.code.as_deref()
}
#[getter]
fn status(&self) -> Option<u16> {
self.status
}
#[getter]
fn details<'py>(&self, py: Python<'py>) -> Bound<'py, PyList> {
self.details.bind(py).clone()
}
fn __str__(&self) -> String {
match (&self.code, self.status) {
(Some(code), _) => format!("{code}: {}", self.message),
(None, Some(status)) => format!("HTTP {status}: {}", self.message),
(None, None) => self.message.clone(),
}
}
fn __repr__(slf: &Bound<'_, Self>) -> PyResult<String> {
let this = slf.get();
let py = slf.py();
let mut parts = vec![PyString::new(py, & this.message).repr() ? .to_string()];
if let Some(code) = &this.code {
parts.push(format!("code={}", PyString::new(py, code).repr() ?));
}
if let Some(status) = this.status {
parts.push(format!("status={status}"));
}
Ok(format!("{}({})", slf.get_type().name() ?, parts.join(", ")))
}
#[allow(clippy::type_complexity)]
fn __reduce__<'py>(
slf: &Bound<'py, Self>,
) -> (
Bound<'py, ::pyo3::types::PyType>,
(String, Option<String>, Option<u16>, Py<PyList>),
) {
let this = slf.get();
let args = (
this.message.clone(),
this.code.clone(),
this.status,
this.details.clone_ref(slf.py()),
);
(slf.get_type(), args)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "BadRequestError",
module = "example._core",
)]
pub struct BadRequestError;
#[::pyo3::pymethods]
impl BadRequestError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "UnauthenticatedError",
module = "example._core",
)]
pub struct UnauthenticatedError;
#[::pyo3::pymethods]
impl UnauthenticatedError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "PermissionDeniedError",
module = "example._core",
)]
pub struct PermissionDeniedError;
#[::pyo3::pymethods]
impl PermissionDeniedError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "NotFoundError",
module = "example._core",
)]
pub struct NotFoundError;
#[::pyo3::pymethods]
impl NotFoundError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "ConflictError",
module = "example._core",
)]
pub struct ConflictError;
#[::pyo3::pymethods]
impl ConflictError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "RateLimitError",
module = "example._core",
)]
pub struct RateLimitError;
#[::pyo3::pymethods]
impl RateLimitError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
#[::pyo3::pyclass(
extends = ExampleError,
frozen,
name = "ServerError",
module = "example._core",
)]
pub struct ServerError;
#[::pyo3::pymethods]
impl ServerError {
#[new]
#[pyo3(signature = (message, code = None, status = None, details = None))]
fn new(
py: Python<'_>,
message: String,
code: Option<String>,
status: Option<u16>,
details: Option<Bound<'_, PyList>>,
) -> PyClassInitializer<Self> {
PyClassInitializer::from(ExampleError::new(py, message, code, status, details))
.add_subclass(Self)
}
}
fn client_exception(py: Python<'_>, e: ClientError) -> PyResult<PyErr> {
let (message, code, status, details) = match e.into_api_error() {
Ok(api) => (api.message, api.code, Some(api.status), api.details),
Err(other) => {
let mut message = other.to_string();
let mut cause = ::std::error::Error::source(&other)
.and_then(::std::error::Error::source);
while let Some(error) = cause {
message.push_str(": ");
message.push_str(&error.to_string());
cause = error.source();
}
(message, None, None, ::std::vec::Vec::new())
}
};
let details = details
.iter()
.map(|d| json_value(py, d))
.collect::<PyResult<::std::vec::Vec<_>>>()?;
let args = (message.clone(),);
let base = PyClassInitializer::from(ExampleError {
message,
code,
status,
details: PyList::new(py, details)?.unbind(),
});
let exception = match status {
Some(400u16) => Bound::new(py, base.add_subclass(BadRequestError))?.into_any(),
Some(401u16) => {
Bound::new(py, base.add_subclass(UnauthenticatedError))?.into_any()
}
Some(403u16) => {
Bound::new(py, base.add_subclass(PermissionDeniedError))?.into_any()
}
Some(404u16) => Bound::new(py, base.add_subclass(NotFoundError))?.into_any(),
Some(409u16) => Bound::new(py, base.add_subclass(ConflictError))?.into_any(),
Some(429u16) => Bound::new(py, base.add_subclass(RateLimitError))?.into_any(),
Some(500..=599) => Bound::new(py, base.add_subclass(ServerError))?.into_any(),
_ => Bound::new(py, base)?.into_any(),
};
exception.setattr("args", args)?;
Ok(PyErr::from_value(exception))
}
fn json_value<'py>(
py: Python<'py>,
value: &::serde_json::Value,
) -> PyResult<Bound<'py, PyAny>> {
use ::serde_json::Value;
Ok(
match value {
Value::Null => py.None().into_bound(py),
Value::Bool(b) => b.into_pyobject(py)?.to_owned().into_any(),
Value::Number(n) => {
match (n.as_i64(), n.as_u64()) {
(Some(i), _) => i.into_pyobject(py)?.into_any(),
(None, Some(u)) => u.into_pyobject(py)?.into_any(),
(None, None) => {
n.as_f64().unwrap_or(f64::NAN).into_pyobject(py)?.into_any()
}
}
}
Value::String(s) => PyString::new(py, s).into_any(),
Value::Array(items) => {
PyList::new(
py,
items
.iter()
.map(|item| json_value(py, item))
.collect::<PyResult<::std::vec::Vec<_>>>()?,
)?
.into_any()
}
Value::Object(map) => {
let dict = PyDict::new(py);
for (key, item) in map {
dict.set_item(key, json_value(py, item)?)?;
}
dict.into_any()
}
},
)
}