use std::{ffi::CStr, marker::PhantomData};
use crate::{impl_::pyclass::PyClassImpl, PyClass};
pub trait PyClassNewTextSignature {
const TEXT_SIGNATURE: &'static str;
}
pub struct PyClassDocGenerator<
ClassT: PyClass,
const HAS_NEW_TEXT_SIGNATURE: bool,
>(PhantomData<ClassT>);
impl<ClassT: PyClass + PyClassNewTextSignature> PyClassDocGenerator<ClassT, true> {
pub const DOC_PIECES: &'static [&'static [u8]] = &[
<ClassT as PyClass>::NAME.as_bytes(),
ClassT::TEXT_SIGNATURE.as_bytes(),
b"\n--\n\n",
<ClassT as PyClassImpl>::RAW_DOC.to_bytes_with_nul(),
];
}
impl<ClassT: PyClass> PyClassDocGenerator<ClassT, false> {
pub const DOC_PIECES: &'static [&'static [u8]] =
&[<ClassT as PyClassImpl>::RAW_DOC.to_bytes_with_nul()];
}
pub const fn doc_bytes_as_cstr(bytes: &'static [u8]) -> &'static ::std::ffi::CStr {
match CStr::from_bytes_with_nul(bytes) {
Ok(cstr) => cstr,
#[cfg(not(from_bytes_with_nul_error))] Err(_) => panic!("invalid pyclass doc"),
#[cfg(from_bytes_with_nul_error)]
Err(std::ffi::FromBytesWithNulError::InteriorNul { .. }) => {
panic!("pyclass doc contains nul bytes")
}
#[cfg(from_bytes_with_nul_error)]
Err(std::ffi::FromBytesWithNulError::NotNulTerminated) => {
panic!("pyclass doc expected to be nul terminated")
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
#[cfg(feature = "macros")]
fn test_doc_generator() {
use crate::impl_::concat::{combine_to_array, combined_len};
#[crate::pyclass(crate = "crate")]
struct MyClass;
#[crate::pymethods(crate = "crate")]
impl MyClass {
#[new]
fn new(x: i32, y: i32) -> Self {
let _ = (x, y); MyClass
}
}
const PIECES: &[&[u8]] = PyClassDocGenerator::<MyClass, true>::DOC_PIECES;
assert_eq!(
&combine_to_array::<{ combined_len(PIECES) }>(PIECES),
b"MyClass(x, y)\n--\n\nA dummy class with signature.\0"
);
const PIECES_WITHOUT_SIGNATURE: &[&[u8]] =
PyClassDocGenerator::<MyClass, false>::DOC_PIECES;
assert_eq!(
&combine_to_array::<{ combined_len(PIECES_WITHOUT_SIGNATURE) }>(
PIECES_WITHOUT_SIGNATURE
),
b"A dummy class with signature.\0"
);
}
#[test]
fn test_doc_bytes_as_cstr() {
let cstr = doc_bytes_as_cstr(b"MyClass\0");
assert_eq!(cstr, c"MyClass");
}
#[test]
#[cfg(from_bytes_with_nul_error)]
#[should_panic(expected = "pyclass doc contains nul bytes")]
fn test_doc_bytes_as_cstr_central_nul() {
doc_bytes_as_cstr(b"MyClass\0Foo");
}
#[test]
#[cfg(from_bytes_with_nul_error)]
#[should_panic(expected = "pyclass doc expected to be nul terminated")]
fn test_doc_bytes_as_cstr_not_nul_terminated() {
doc_bytes_as_cstr(b"MyClass");
}
}