blob: 3eaa843a3042b9c0d813dd1dc68bcf4927487c32 [file]
// Licensed to the Apache Software Foundation (ASF) under one
// or more contributor license agreements. See the NOTICE file
// distributed with this work for additional information
// regarding copyright ownership. The ASF licenses this file
// to you under the Apache License, Version 2.0 (the
// "License"); you may not use this file except in compliance
// with the License. You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing,
// software distributed under the License is distributed on an
// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
use std::collections::BTreeMap;
use std::collections::hash_map::DefaultHasher;
use std::hash::{Hash, Hasher};
use iggy::prelude::{
HeaderField, HeaderKey as RustHeaderKey, HeaderKind, HeaderValue as RustHeaderValue,
};
use pyo3::PyClass;
use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::pyclass::CompareOp;
use pyo3::types::{PyBool, PyBytes, PyDict, PyFloat, PyInt, PyString, PyTuple};
use pyo3_stub_gen::derive::{gen_stub_pyclass, gen_stub_pyclass_complex_enum, gen_stub_pymethods};
type RustUserHeaders = BTreeMap<RustHeaderKey, RustHeaderValue>;
#[derive(PartialEq, Eq, Hash)]
struct HeaderIdentity {
kind: u8,
value: Vec<u8>,
}
impl HeaderIdentity {
fn raw(value: Vec<u8>) -> Self {
Self {
kind: HeaderKind::Raw.as_code(),
value,
}
}
fn string(value: Vec<u8>) -> Self {
Self {
kind: HeaderKind::String.as_code(),
value,
}
}
fn bool(value: bool) -> Self {
Self {
kind: HeaderKind::Bool.as_code(),
value: vec![u8::from(value)],
}
}
fn int8(value: i8) -> Self {
Self {
kind: HeaderKind::Int8.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn int16(value: i16) -> Self {
Self {
kind: HeaderKind::Int16.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn int32(value: i32) -> Self {
Self {
kind: HeaderKind::Int32.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn int64(value: i64) -> Self {
Self {
kind: HeaderKind::Int64.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn int128(value: i128) -> Self {
Self {
kind: HeaderKind::Int128.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn uint8(value: u8) -> Self {
Self {
kind: HeaderKind::Uint8.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn uint16(value: u16) -> Self {
Self {
kind: HeaderKind::Uint16.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn uint32(value: u32) -> Self {
Self {
kind: HeaderKind::Uint32.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn uint64(value: u64) -> Self {
Self {
kind: HeaderKind::Uint64.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn uint128(value: u128) -> Self {
Self {
kind: HeaderKind::Uint128.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn float32(value: f32) -> Self {
Self {
kind: HeaderKind::Float32.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
fn float64(value: f64) -> Self {
Self {
kind: HeaderKind::Float64.as_code(),
value: value.to_le_bytes().to_vec(),
}
}
}
#[gen_stub_pyclass_complex_enum]
#[pyclass]
/// Typed key for an Iggy user header.
///
/// Use these constructors when the header key must preserve an explicit
/// wire type instead of using the common string-key dictionary form.
pub enum HeaderKey {
/// Raw bytes key. The byte length must be 1..=255.
Raw { value: Py<PyBytes> },
/// UTF-8 string key. The encoded byte length must be 1..=255.
String { value: String },
/// Boolean key.
Bool { value: bool },
/// Signed 8-bit integer key.
Int8 { value: i8 },
/// Signed 16-bit integer key.
Int16 { value: i16 },
/// Signed 32-bit integer key.
Int32 { value: i32 },
/// Signed 64-bit integer key.
Int64 { value: i64 },
/// Signed 128-bit integer key.
Int128 { value: i128 },
/// Unsigned 8-bit integer key.
UnsignedInt8 { value: u8 },
/// Unsigned 16-bit integer key.
UnsignedInt16 { value: u16 },
/// Unsigned 32-bit integer key.
UnsignedInt32 { value: u32 },
/// Unsigned 64-bit integer key.
UnsignedInt64 { value: u64 },
/// Unsigned 128-bit integer key.
UnsignedInt128 { value: u128 },
/// 32-bit floating point key.
Float32 { value: f32 },
/// 64-bit floating point key.
Float64 { value: f64 },
}
#[gen_stub_pyclass_complex_enum]
#[pyclass]
/// Typed value for an Iggy user header.
///
/// Use these constructors when the header value must preserve an explicit
/// wire type instead of using the common Python scalar dictionary form.
pub enum HeaderValue {
/// Raw bytes value. The byte length must be 1..=255.
Raw { value: Py<PyBytes> },
/// UTF-8 string value. The encoded byte length must be 1..=255.
String { value: String },
/// Boolean value.
Bool { value: bool },
/// Signed 8-bit integer value.
Int8 { value: i8 },
/// Signed 16-bit integer value.
Int16 { value: i16 },
/// Signed 32-bit integer value.
Int32 { value: i32 },
/// Signed 64-bit integer value.
Int64 { value: i64 },
/// Signed 128-bit integer value.
Int128 { value: i128 },
/// Unsigned 8-bit integer value.
UnsignedInt8 { value: u8 },
/// Unsigned 16-bit integer value.
UnsignedInt16 { value: u16 },
/// Unsigned 32-bit integer value.
UnsignedInt32 { value: u32 },
/// Unsigned 64-bit integer value.
UnsignedInt64 { value: u64 },
/// Unsigned 128-bit integer value.
UnsignedInt128 { value: u128 },
/// 32-bit floating point value.
Float32 { value: f32 },
/// 64-bit floating point value.
Float64 { value: f64 },
}
trait PyHeaderFieldToRust: PyClass + Sized {
type RustType;
fn from_pyheader(py: Python<'_>, value: &Self) -> PyResult<Self::RustType>;
fn from_plain(field: &Bound<'_, PyAny>) -> PyResult<Option<Self::RustType>>;
}
fn py_header_field_to_rust<T: PyHeaderFieldToRust>(
py: Python<'_>,
field: &Bound<'_, PyAny>,
) -> PyResult<T::RustType> {
if let Ok(header) = field.extract::<PyRef<'_, T>>() {
return T::from_pyheader(py, &header);
}
T::from_plain(field)?.ok_or_else(|| {
PyValueError::new_err(
"User header must be str, bytes, bool, int, float, HeaderKey, or HeaderValue",
)
})
}
trait ToPlain {
fn to_plain<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>>;
}
/// Borrows a typed header ([`HeaderKey`] or [`HeaderValue`]) together with the
/// GIL token for direct conversion to a plain Python scalar.
struct HeaderToPlainRef<'py, 'value, T: ToPlain> {
py: Python<'py>,
value: &'value T,
}
impl<'py, T: ToPlain> TryFrom<HeaderToPlainRef<'py, '_, T>> for Bound<'py, PyAny> {
type Error = PyErr;
fn try_from(inner: HeaderToPlainRef<'py, '_, T>) -> PyResult<Self> {
let HeaderToPlainRef { py, value } = inner;
value.to_plain(py)
}
}
macro_rules! header_type_impl {
($py_ty:ident) => {
paste::paste! {
#[gen_stub_pymethods]
#[pymethods]
impl $py_ty {
pub fn __hash__(&self, py: Python<'_>) -> PyResult<isize> {
Ok(py_hash(header_identity_hash(self.identity(py)?)))
}
pub fn __richcmp__(
&self,
py: Python<'_>,
other: &Bound<'_, PyAny>,
op: CompareOp,
) -> PyResult<Py<PyAny>> {
let identity = self.identity(py)?;
let Ok(other_header) = other.extract::<PyRef<'_, $py_ty>>() else {
return match op {
CompareOp::Eq => {
Ok(false.into_pyobject(py)?.to_owned().into_any().unbind())
}
CompareOp::Ne => {
Ok(true.into_pyobject(py)?.to_owned().into_any().unbind())
}
_ => Ok(py.NotImplemented()),
};
};
let result = match op {
CompareOp::Eq => identity == other_header.identity(py)?,
CompareOp::Ne => identity != other_header.identity(py)?,
_ => return Ok(py.NotImplemented()),
};
Ok(result.into_pyobject(py)?.to_owned().into_any().unbind())
}
pub fn __repr__(&self, py: Python<'_>) -> PyResult<String> {
self.repr(py)
}
}
struct [<Py $py_ty Ref>]<'py, 'value> {
py: Python<'py>,
value: &'value $py_ty,
}
impl TryFrom<[<Py $py_ty Ref>]<'_, '_>> for [<Rust $py_ty>] {
type Error = PyErr;
fn try_from(value: [<Py $py_ty Ref>]<'_, '_>) -> PyResult<Self> {
let [<Py $py_ty Ref>] { py, value } = value;
match value {
$py_ty::Raw { value } => {
<[<Rust $py_ty>]>::try_from(value.extract::<Vec<u8>>(py)?)
.map_err(to_value_error)
}
$py_ty::String { value } => {
<[<Rust $py_ty>]>::try_from(value.as_str()).map_err(to_value_error)
}
$py_ty::Bool { value } => Ok((*value).into()),
$py_ty::Int8 { value } => Ok((*value).into()),
$py_ty::Int16 { value } => Ok((*value).into()),
$py_ty::Int32 { value } => Ok((*value).into()),
$py_ty::Int64 { value } => Ok((*value).into()),
$py_ty::Int128 { value } => Ok((*value).into()),
$py_ty::UnsignedInt8 { value } => Ok((*value).into()),
$py_ty::UnsignedInt16 { value } => Ok((*value).into()),
$py_ty::UnsignedInt32 { value } => Ok((*value).into()),
$py_ty::UnsignedInt64 { value } => Ok((*value).into()),
$py_ty::UnsignedInt128 { value } => Ok((*value).into()),
$py_ty::Float32 { value } => checked_float32(*value),
$py_ty::Float64 { value } => checked_float64(*value),
}
}
}
struct [<Rust $py_ty Ref>]<'py, 'value> {
py: Python<'py>,
value: &'value [<Rust $py_ty>],
}
impl<'py> TryFrom<[<Rust $py_ty Ref>]<'py, '_>> for Bound<'py, $py_ty> {
type Error = PyErr;
fn try_from(value: [<Rust $py_ty Ref>]<'py, '_>) -> PyResult<Self> {
let [<Rust $py_ty Ref>] { py, value } = value;
let value = match value.kind() {
HeaderKind::Raw => $py_ty::Raw {
value: PyBytes::new(py, value.as_raw().map_err(to_value_error)?)
.unbind(),
},
HeaderKind::String => $py_ty::String {
value: value.as_str().map_err(to_value_error)?.to_string(),
},
HeaderKind::Bool => $py_ty::Bool {
value: value.as_bool().map_err(to_value_error)?,
},
HeaderKind::Int8 => $py_ty::Int8 {
value: value.as_int8().map_err(to_value_error)?,
},
HeaderKind::Int16 => $py_ty::Int16 {
value: value.as_int16().map_err(to_value_error)?,
},
HeaderKind::Int32 => $py_ty::Int32 {
value: value.as_int32().map_err(to_value_error)?,
},
HeaderKind::Int64 => $py_ty::Int64 {
value: value.as_int64().map_err(to_value_error)?,
},
HeaderKind::Int128 => $py_ty::Int128 {
value: value.as_int128().map_err(to_value_error)?,
},
HeaderKind::Uint8 => $py_ty::UnsignedInt8 {
value: value.as_uint8().map_err(to_value_error)?,
},
HeaderKind::Uint16 => $py_ty::UnsignedInt16 {
value: value.as_uint16().map_err(to_value_error)?,
},
HeaderKind::Uint32 => $py_ty::UnsignedInt32 {
value: value.as_uint32().map_err(to_value_error)?,
},
HeaderKind::Uint64 => $py_ty::UnsignedInt64 {
value: value.as_uint64().map_err(to_value_error)?,
},
HeaderKind::Uint128 => $py_ty::UnsignedInt128 {
value: value.as_uint128().map_err(to_value_error)?,
},
HeaderKind::Float32 => {
let v = value.as_float32().map_err(to_value_error)?;
if !v.is_finite() {
return Err(PyValueError::new_err(
"User header with non-finite Float32 value",
));
}
$py_ty::Float32 { value: v }
},
HeaderKind::Float64 => {
let v = value.as_float64().map_err(to_value_error)?;
if !v.is_finite() {
return Err(PyValueError::new_err(
"User header with non-finite Float64 value",
));
}
$py_ty::Float64 { value: v }
},
};
value.into_pyobject(py)
}
}
impl $py_ty {
fn identity(&self, py: Python<'_>) -> PyResult<HeaderIdentity> {
match self {
$py_ty::Raw { value } => {
Ok(HeaderIdentity::raw(value.extract::<Vec<u8>>(py)?))
}
$py_ty::String { value } => {
Ok(HeaderIdentity::string(value.as_bytes().to_vec()))
}
$py_ty::Bool { value } => Ok(HeaderIdentity::bool(*value)),
$py_ty::Int8 { value } => Ok(HeaderIdentity::int8(*value)),
$py_ty::Int16 { value } => Ok(HeaderIdentity::int16(*value)),
$py_ty::Int32 { value } => Ok(HeaderIdentity::int32(*value)),
$py_ty::Int64 { value } => Ok(HeaderIdentity::int64(*value)),
$py_ty::Int128 { value } => Ok(HeaderIdentity::int128(*value)),
$py_ty::UnsignedInt8 { value } => Ok(HeaderIdentity::uint8(*value)),
$py_ty::UnsignedInt16 { value } => Ok(HeaderIdentity::uint16(*value)),
$py_ty::UnsignedInt32 { value } => Ok(HeaderIdentity::uint32(*value)),
$py_ty::UnsignedInt64 { value } => Ok(HeaderIdentity::uint64(*value)),
$py_ty::UnsignedInt128 { value } => Ok(HeaderIdentity::uint128(*value)),
$py_ty::Float32 { value } => Ok(HeaderIdentity::float32(*value)),
$py_ty::Float64 { value } => Ok(HeaderIdentity::float64(*value)),
}
}
fn repr(&self, py: Python<'_>) -> PyResult<String> {
match self {
$py_ty::Raw { value } => Ok(format!(
concat!(stringify!($py_ty), ".Raw({})"),
value.bind(py).repr()?.extract::<String>()?
)),
$py_ty::String { value } => Ok(format!(
concat!(stringify!($py_ty), ".String({:?})"),
value
)),
$py_ty::Bool { value } => Ok(format!(
concat!(stringify!($py_ty), ".Bool({})"),
value
)),
$py_ty::Int8 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Int8({})"),
value
)),
$py_ty::Int16 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Int16({})"),
value
)),
$py_ty::Int32 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Int32({})"),
value
)),
$py_ty::Int64 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Int64({})"),
value
)),
$py_ty::Int128 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Int128({})"),
value
)),
$py_ty::UnsignedInt8 { value } => Ok(format!(
concat!(stringify!($py_ty), ".UnsignedInt8({})"),
value
)),
$py_ty::UnsignedInt16 { value } => Ok(format!(
concat!(stringify!($py_ty), ".UnsignedInt16({})"),
value
)),
$py_ty::UnsignedInt32 { value } => Ok(format!(
concat!(stringify!($py_ty), ".UnsignedInt32({})"),
value
)),
$py_ty::UnsignedInt64 { value } => Ok(format!(
concat!(stringify!($py_ty), ".UnsignedInt64({})"),
value
)),
$py_ty::UnsignedInt128 { value } => Ok(format!(
concat!(stringify!($py_ty), ".UnsignedInt128({})"),
value
)),
$py_ty::Float32 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Float32({:?})"),
value
)),
$py_ty::Float64 { value } => Ok(format!(
concat!(stringify!($py_ty), ".Float64({:?})"),
value
)),
}
}
}
impl PyHeaderFieldToRust for $py_ty {
type RustType = [<Rust $py_ty>];
fn from_pyheader(py: Python<'_>, value: &Self) -> PyResult<[<Rust $py_ty>]> {
<[<Rust $py_ty>]>::try_from([<Py $py_ty Ref>] { py, value })
}
fn from_plain(field: &Bound<'_, PyAny>) -> PyResult<Option<[<Rust $py_ty>]>> {
plain_to_rust_header_field(field)
}
}
impl ToPlain for $py_ty {
fn to_plain<'py>(&self, py: Python<'py>) -> PyResult<Bound<'py, PyAny>> {
match self {
Self::Raw { value } => Ok(value.bind(py).clone().into_any()),
Self::String { value } => Ok(value.clone().into_pyobject(py)?.into_any()),
Self::Bool { value } => Ok((*value).into_pyobject(py)?.to_owned().into_any()),
Self::Int8 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::Int16 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::Int32 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::Int64 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::Int128 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::UnsignedInt8 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::UnsignedInt16 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::UnsignedInt32 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::UnsignedInt64 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::UnsignedInt128 { value } => Ok((*value).into_pyobject(py)?.into_any()),
Self::Float32 { value } => {
if !value.is_finite() {
return Err(PyValueError::new_err(
"User header with non-finite Float32 value",
));
}
Ok(f64::from(*value).into_pyobject(py)?.into_any())
},
Self::Float64 { value } => {
if !value.is_finite() {
return Err(PyValueError::new_err(
"User header with non-finite Float64 value",
));
}
Ok((*value).into_pyobject(py)?.into_any())
},
}
}
}
}
};
}
header_type_impl!(HeaderKey);
header_type_impl!(HeaderValue);
pub(crate) fn py_user_headers_to_rust(
py: Python<'_>,
mapping: &Bound<'_, PyAny>,
) -> PyResult<RustUserHeaders> {
let mut rust_headers = BTreeMap::new();
for item in mapping.call_method0("items")?.try_iter()? {
let item = item?;
let pair: &Bound<'_, PyTuple> = item.cast()?;
let key = pair.get_item(0)?;
let value = pair.get_item(1)?;
let key = py_header_field_to_rust::<HeaderKey>(py, &key)?;
let value = py_header_field_to_rust::<HeaderValue>(py, &value)?;
if rust_headers.insert(key, value).is_some() {
return Err(PyValueError::new_err(
"Duplicate user header key: each header key must be unique",
));
}
}
Ok(rust_headers)
}
pub(crate) fn rust_user_headers_to_py<'a>(
py: Python<'a>,
headers: RustUserHeaders,
) -> PyResult<Bound<'a, UserHeaders>> {
let result = Bound::new(py, UserHeaders)?;
let mapping = result.as_any();
for (key, value) in headers {
let key = Bound::<HeaderKey>::try_from(RustHeaderKeyRef { py, value: &key })?;
let value = Bound::<HeaderValue>::try_from(RustHeaderValueRef { py, value: &value })?;
mapping.set_item(key, value)?;
}
Ok(result)
}
/// User headers dictionary returned by `ReceiveMessage.user_headers`.
///
/// This is a regular `dict[HeaderKey, HeaderValue]` (so all mapping
/// operations work) that additionally exposes `to_scalar_dict` for the convenient
/// scalar form.
#[gen_stub_pyclass]
#[pyclass(extends=PyDict)]
pub struct UserHeaders;
#[gen_stub_pymethods]
#[pymethods]
impl UserHeaders {
/// Wraps a mapping so its entries gain the `to_scalar_dict` helper.
///
/// Accepts a dict whose keys and values can each independently be
/// `HeaderKey`/`HeaderValue` or a plain scalar (`str | bytes | bool |
/// int | float`). The inherited `dict` initializer copies the provided
/// mapping.
#[new]
#[pyo3(signature = (mapping=None))]
pub fn new(
#[gen_stub(override_type(type_repr = "dict | None"))] mapping: Option<&Bound<'_, PyAny>>,
) -> PyResult<Self> {
if let Some(mapping) = mapping {
let py = mapping.py();
py_user_headers_to_rust(py, mapping)?;
}
Ok(UserHeaders)
}
fn __setitem__(
slf: &Bound<'_, Self>,
key: &Bound<'_, PyAny>,
value: &Bound<'_, PyAny>,
) -> PyResult<()> {
let py = key.py();
py_header_field_to_rust::<HeaderKey>(py, key)?;
py_header_field_to_rust::<HeaderValue>(py, value)?;
slf.as_any().cast::<PyDict>()?.set_item(key, value)?;
Ok(())
}
/// Converts these headers into the convenient plain dictionary form.
///
/// Returns an error if two distinct typed keys map to the same plain
/// Python scalar (e.g., `UnsignedInt8(1)` and `UnsignedInt16(1)` both
/// become `int(1)`), or if a stored field cannot be decoded.
#[gen_stub(override_return_type(
type_repr = "dict[str | bytes | bool | int | float, str | bytes | bool | int | float]"
))]
pub fn to_scalar_dict<'a>(slf: &Bound<'a, Self>) -> PyResult<Bound<'a, PyDict>> {
let py = slf.py();
let dict = slf.as_any().cast::<PyDict>()?;
let result = PyDict::new(py);
for (key, value) in dict.iter() {
let plain_key = py_header_to_plain::<HeaderKey>(py, &key)?;
let plain_value = py_header_to_plain::<HeaderValue>(py, &value)?;
if result.contains(&plain_key)? {
return Err(PyValueError::new_err(
"Distinct typed header keys produce the same plain Python scalar; this conversion is lossy and cannot proceed",
));
}
result.set_item(plain_key, plain_value)?;
}
Ok(result)
}
}
fn py_header_to_plain<'py, T: ToPlain + PyClass>(
py: Python<'py>,
any: &Bound<'py, PyAny>,
) -> PyResult<Bound<'py, PyAny>> {
if let Ok(header) = any.extract::<PyRef<'_, T>>() {
return Bound::<PyAny>::try_from(HeaderToPlainRef {
py,
value: &*header,
});
}
if any.is_instance_of::<PyString>()
|| any.is_instance_of::<PyBytes>()
|| any.is_instance_of::<PyInt>()
|| any.is_instance_of::<PyFloat>()
{
return Ok(any.clone());
}
Err(PyValueError::new_err(
"User header must be str, bytes, bool, int, float, HeaderKey, or HeaderValue",
))
}
/// Converts a plain Python scalar into a header field (used for both keys and
/// values). Returns `Ok(None)` when the object is not a supported plain type so
/// the caller can raise a message naming keys vs values; genuine range/length
/// errors are returned as `Err`.
fn plain_to_rust_header_field<T>(obj: &Bound<'_, PyAny>) -> PyResult<Option<HeaderField<T>>> {
if obj.is_instance_of::<PyBool>() {
return Ok(Some(obj.extract::<bool>()?.into()));
}
if obj.is_instance_of::<PyInt>() {
return int_to_rust_header_field(obj).map(Some);
}
if obj.is_instance_of::<PyFloat>() {
return float_to_rust_header_field(obj.extract::<f64>()?).map(Some);
}
if obj.is_instance_of::<PyString>() {
return HeaderField::try_from(obj.extract::<String>()?)
.map(Some)
.map_err(to_value_error);
}
if obj.is_instance_of::<PyBytes>() {
return HeaderField::try_from(obj.extract::<Vec<u8>>()?)
.map(Some)
.map_err(to_value_error);
}
Ok(None)
}
fn int_to_rust_header_field<T>(obj: &Bound<'_, PyAny>) -> PyResult<HeaderField<T>> {
if let Ok(value) = obj.extract::<u8>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<u16>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<u32>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<u64>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<u128>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<i8>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<i16>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<i32>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<i64>() {
return Ok(value.into());
}
if let Ok(value) = obj.extract::<i128>() {
return Ok(value.into());
}
Err(PyValueError::new_err(
"User header int values must fit within the 128-bit range",
))
}
fn float_to_rust_header_field<T>(value: f64) -> PyResult<HeaderField<T>> {
if !value.is_finite() {
return Err(PyValueError::new_err(
"User header float values must be finite",
));
}
let narrowed = value as f32;
if f64::from(narrowed) == value {
checked_float32(narrowed)
} else {
Ok(value.into())
}
}
fn checked_float32<T>(value: f32) -> PyResult<HeaderField<T>> {
if !value.is_finite() {
return Err(PyValueError::new_err(
"Float32 header must be a finite value within the 32-bit float range",
));
}
Ok(value.into())
}
fn checked_float64<T>(value: f64) -> PyResult<HeaderField<T>> {
if !value.is_finite() {
return Err(PyValueError::new_err("Float64 header value must be finite"));
}
Ok(value.into())
}
fn header_identity_hash(identity: HeaderIdentity) -> u64 {
let mut hasher = DefaultHasher::new();
identity.hash(&mut hasher);
hasher.finish()
}
fn py_hash(hash: u64) -> isize {
let hash = hash as isize;
if hash == -1 { -2 } else { hash }
}
fn to_value_error(error: impl ToString) -> PyErr {
PyValueError::new_err(error.to_string())
}