mod inner_types {
#[macro_export]
macro_rules! unpack_pytuple {
($t:ident; ($($p:tt,)+) ) => {{
use rustypy::{PyArg, PyTuple};
use rustypy::pytypes::abort_and_exit;
let mut cnt = 0;
let mut pytuple = unsafe { PyTuple::from_ptr($t) };
($(
unpack_pytuple!(pytuple; cnt; elem: $p)
,)*)
}};
($t:ident; $i:ident; elem: ($($p:tt,)+)) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::PyTuple(val) => {
$i += 1;
if $i == 0 {}; let mut val = unsafe { PyTuple::from_ptr(val) };
let mut cnt = 0;
($(
unpack_pytuple!(val; cnt; elem: $p)
,)*)
},
_ => abort_and_exit("failed while extracting a PyTuple inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: {PyDict{$u:tt}}) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::PyDict(val) => {
$i += 1;
if $i == 0 {}; unpack_pydict!(val; PyDict{$u})
},
_ => abort_and_exit("failed while extracting a PyDict inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: {PyList{$($u:tt)*}}) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::PyList(val) => {
$i += 1;
if $i == 0 {}; unpack_pylist!(val; PyList{$($u)*})
},
_ => abort_and_exit("failed while extracting a PyList inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: PyBool) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::PyBool(val) => {
$i += 1;
if $i == 0 {}; val.to_bool()
},
_ => abort_and_exit("failed while extracting a PyBool inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: PyString) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::PyString(val) => {
$i += 1;
if $i == 0 {}; val.to_string()
},
_ => abort_and_exit("failed while extracting a PyString inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: I64) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::I64(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a i64 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: I32) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::I32(val) => {
$i += 1;
if $i == 0 {}; val.clone()
},
_ => abort_and_exit("failed while extracting a i32 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: I16) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::I16(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a i16 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: I8) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::I8(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a i8 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: U32) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::U32(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a u32 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: U16) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::U16(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a u16 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: U8) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::U8(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a u8 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: F32) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::F32(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a f32 inside a PyTuple"),
}
}};
($t:ident; $i:ident; elem: F64) => {{
let e = $t.replace_elem($i).unwrap();
match e {
PyArg::F64(val) => {
$i += 1;
if $i == 0 {}; val
},
_ => abort_and_exit("failed while extracting a f64 inside a PyTuple"),
}
}};
}
#[macro_export]
macro_rules! unpack_pylist {
( $pylist:ident; PyList { $o:tt { $($t:tt)* } } ) => {{
use rustypy::{PyList, PyArg};
use rustypy::pytypes::abort_and_exit;
use std::collections::VecDeque;
let mut unboxed = unsafe { PyList::from_ptr($pylist) };
let mut list = VecDeque::with_capacity(unboxed.len());
for _ in 0..unboxed.len() {
match unboxed.pop() {
Some(PyArg::$o(val)) => {
let inner = unpack_pylist!(val; $o { $($t)* });
list.push_front(inner);
},
Some(_) => abort_and_exit("failed while converting pylist to vec"),
None => {}
}
};
Vec::from(list)
}};
( $pytuple:ident; PyTuple { $t:tt } ) => {{
unpack_pytuple!($pytuple; $t)
}};
( $pylist:ident; PyList{$t:tt => $type_:ty} ) => {{
use rustypy::{PyList, PyArg};
use rustypy::pytypes::abort_and_exit;
use std::collections::VecDeque;
let mut unboxed = unsafe { PyList::from_ptr($pylist) };
let mut list = VecDeque::with_capacity(unboxed.len());
for _ in 0..unboxed.len() {
match unboxed.pop() {
Some(PyArg::$t(val)) => { list.push_front(<$type_>::from(val)); },
Some(_) => abort_and_exit("failed while converting pylist to vec"),
None => {}
}
};
Vec::from(list)
}};
( $pydict:ident; PyDict{$t:tt} ) => {{
unpack_pydict!( $pydict; PyDict{$t} )
}};
}
#[macro_export]
macro_rules! unpack_pydict {
( $pydict:ident; PyDict{($kt:ty, $o:tt { $($t:tt)* })} ) => {{
use rustypy::{PyArg, PyDict};
use rustypy::pytypes::abort_and_exit;
use std::collections::HashMap;
let mut unboxed = unsafe { *(Box::from_raw($pydict as *mut PyDict<$kt>)) };
let mut dict = HashMap::new();
for (k, v) in unboxed.drain() {
match v {
PyArg::$o(val) => {
let inner = unpack_pydict!(val; $o { $($t)* });
dict.insert(k, inner);
}
_ => abort_and_exit("failed while converting PyDict to HashMap")
}
}
dict
}};
( $pytuple:ident; PyTuple { $t:tt } ) => {{
unpack_pytuple!($pytuple; $t)
}};
( $pylist:ident; PyList{ $($u:tt)* } ) => {{
unpack_pylist!( $pylist; PyList{ $($u)* } )
}};
( $pydict:ident; PyDict{($kt:ty, $t:tt => $type_:ty)} ) => {{
use rustypy::{PyArg, PyDict};
use rustypy::pytypes::abort_and_exit;
use std::collections::HashMap;
let mut unboxed = unsafe { *(Box::from_raw($pydict as *mut PyDict<$kt>)) };
let mut dict = HashMap::new();
for (k, v) in unboxed.drain() {
match v {
PyArg::$t(val) => { dict.insert(k, <$type_>::from(val)); },
_ => abort_and_exit("failed while converting PyDict to HashMap"),
}
}
dict
}};
}
}
#[macro_export]
macro_rules! unpack_pytype {
($t:ident; ($($p:tt,)+)) => {
unpack_pytuple!($t; ($($p,)*))
};
($pylist:ident; PyList{$t:tt => $type_:ty}) => {
unpack_pylist!($pylist; PyList{$t => $type_})
};
($pylist:ident; PyList { $o:tt { $($t:tt)* } }) => {
unpack_pylist!($pylist; PyList { $o { $($t)* } })
};
($pydict:ident; PyDict{($kt:ty, $o:tt { $($t:tt)* })}) => {
unpack_pydict!($pydict; PyDict{($kt, $o { $($t)* })})
};
($pydict:ident; PyDict{($kt:ty, $t:tt => $type_:ty)}) => {
unpack_pydict!($pydict; PyDict{($kt, $t => $type_)})
};
}
#[cfg(test)]
mod tests {
use crate as rustypy;
use rustypy::*;
#[test]
fn pytuple_macro() {
let pytuple = pytuple!(
PyArg::PyBool(PyBool::from(false)),
PyArg::PyString(PyString::from("test")),
PyArg::I64(55i64)
)
.into_raw();
let unpacked = unpack_pytype!(pytuple; (PyBool, PyString, I64,));
assert_eq!((false, String::from("test"), 55i64), unpacked);
}
#[test]
fn unpack_pylist_macro() {
use std::iter::FromIterator;
let nested = PyList::from_iter(vec![
pytuple!(
PyArg::PyList(PyList::from_iter(vec![1i32, 2, 3]).into_raw()),
PyArg::F32(0.1)
),
pytuple!(
PyArg::PyList(PyList::from_iter(vec![3i32, 2, 1]).into_raw()),
PyArg::F32(0.2)
),
])
.into_raw();
let unpacked = unpack_pytype!(nested; PyList{PyTuple{({PyList{I32 => i32}}, F32,)}});
assert_eq!(vec![(vec![1, 2, 3], 0.1), (vec![3, 2, 1], 0.2)], unpacked);
}
}