use pyo3::{
PyTypeInfo,
ffi::PyTypeObject,
intern,
prelude::*,
sync::PyOnceLock,
types::{DerefToPyAny, IntoPyDict, PyDict, PyType},
};
#[expect(unused_imports)] use crate::PyCodecClassMethods;
use crate::{PyCodecClass, sealed::Sealed};
#[repr(transparent)]
pub struct PyCodec {
_codec: PyAny,
}
pub trait PyCodecMethods<'py>: Sealed {
fn encode(&self, buf: Borrowed<'_, 'py, PyAny>) -> Result<Bound<'py, PyAny>, PyErr>;
fn decode(
&self,
buf: Borrowed<'_, 'py, PyAny>,
out: Option<Borrowed<'_, 'py, PyAny>>,
) -> Result<Bound<'py, PyAny>, PyErr>;
fn get_config(&self) -> Result<Bound<'py, PyDict>, PyErr>;
fn class(&self) -> Bound<'py, PyCodecClass>;
}
impl<'py> PyCodecMethods<'py> for Bound<'py, PyCodec> {
fn encode(&self, buf: Borrowed<'_, 'py, PyAny>) -> Result<Bound<'py, PyAny>, PyErr> {
let py = self.py();
self.as_any().call_method1(intern!(py, "encode"), (buf,))
}
fn decode(
&self,
buf: Borrowed<'_, 'py, PyAny>,
out: Option<Borrowed<'_, 'py, PyAny>>,
) -> Result<Bound<'py, PyAny>, PyErr> {
let py = self.as_any().py();
self.as_any().call_method(
intern!(py, "decode"),
(buf,),
Some(&[(intern!(py, "out"), out)].into_py_dict(py)?),
)
}
fn get_config(&self) -> Result<Bound<'py, PyDict>, PyErr> {
let py = self.as_any().py();
self.as_any()
.call_method0(intern!(py, "get_config"))?
.extract()
}
#[expect(clippy::expect_used)]
fn class(&self) -> Bound<'py, PyCodecClass> {
self.as_any()
.get_type()
.extract()
.expect("Codec's class must be a CodecClass")
}
}
impl Sealed for Bound<'_, PyCodec> {}
#[doc(hidden)]
impl DerefToPyAny for PyCodec {}
#[doc(hidden)]
#[expect(unsafe_code)]
unsafe impl PyTypeInfo for PyCodec {
const MODULE: Option<&'static str> = Some("numcodecs.abc");
const NAME: &'static str = "Codec";
#[inline]
fn type_object_raw(py: Python) -> *mut PyTypeObject {
static CODEC_TYPE: PyOnceLock<Py<PyType>> = PyOnceLock::new();
let ty = CODEC_TYPE.import(py, "numcodecs.abc", "Codec");
#[expect(clippy::expect_used)]
let ty = ty.expect("failed to access the `numpy.abc.Codec` type object");
ty.as_type_ptr()
}
}