use pyo3::exceptions::PyValueError;
use pyo3::prelude::*;
use pyo3::types::{PyBool, PyDict, PyList};
use serde_json::{Map, Number, Value};
pub trait ToPython {
fn to_python(&self, py: Python) -> PyResult<PyObject>;
}
pub trait FromPython: Sized {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self>;
}
impl ToPython for Value {
fn to_python(&self, py: Python) -> PyResult<PyObject> {
match self {
Value::Null => Ok(py.None()),
Value::Bool(b) => Ok(b.into_py(py)),
Value::Number(n) => {
if let Some(i) = n.as_i64() {
Ok(i.into_py(py))
} else if let Some(f) = n.as_f64() {
Ok(f.into_py(py))
} else {
Ok(py.None())
}
}
Value::String(s) => Ok(s.into_py(py)),
Value::Array(arr) => {
let list = PyList::empty_bound(py);
for item in arr {
list.append(item.to_python(py)?)?;
}
Ok(list.into_py(py))
}
Value::Object(map) => {
let dict = PyDict::new_bound(py);
for (k, v) in map {
dict.set_item(k, v.to_python(py)?)?;
}
Ok(dict.into_py(py))
}
}
}
}
impl FromPython for bool {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for i64 {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for i32 {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for u64 {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for u32 {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for f64 {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for f32 {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for String {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
obj.extract()
}
}
impl FromPython for Value {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
if obj.is_none() {
return Ok(Value::Null);
}
if obj.is_instance_of::<PyBool>() {
let b = bool::from_python(obj)?;
return Ok(Value::Bool(b));
}
if let Ok(i) = i64::from_python(obj) {
return Ok(Value::Number(Number::from(i)));
}
if let Ok(i) = i32::from_python(obj) {
return Ok(Value::Number(Number::from(i)));
}
if let Ok(u) = u64::from_python(obj) {
return Ok(Value::Number(Number::from(u)));
}
if let Ok(u) = u32::from_python(obj) {
return Ok(Value::Number(Number::from(u)));
}
if let Ok(f) = f64::from_python(obj) {
if let Some(n) = Number::from_f64(f) {
return Ok(Value::Number(n));
} else {
return Err(PyErr::new::<PyValueError, _>(
"NaN/Inf encountered in float conversion",
));
}
}
if let Ok(f) = f32::from_python(obj) {
if let Some(n) = Number::from_f64(f64::from(f)) {
return Ok(Value::Number(n));
} else {
return Err(PyErr::new::<PyValueError, _>(
"NaN/Inf encountered in float conversion",
));
}
}
if let Ok(s) = String::from_python(obj) {
return Ok(Value::String(s));
}
if let Ok(list) = obj.downcast::<PyList>() {
let mut vec = Vec::new();
for item in list {
vec.push(Value::from_python(&item)?);
}
return Ok(Value::Array(vec));
}
if let Ok(dict) = obj.downcast::<PyDict>() {
let mut map = Map::new();
for (k, v) in dict {
let key = String::from_python(k.as_any())?;
map.insert(key, Value::from_python(&v)?);
}
return Ok(Value::Object(map));
}
Ok(Value::String(obj.to_string()))
}
}
impl FromPython for Vec<u8> {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
let bytes = obj.call_method0("tobytes")?;
bytes.extract()
}
}
impl FromPython for Vec<Value> {
fn from_python(obj: &Bound<'_, PyAny>) -> PyResult<Self> {
let seq = obj.downcast::<pyo3::types::PySequence>()?;
let mut vec = Vec::new();
for item in seq.iter()? {
vec.push(Value::from_python(&item?)?);
}
Ok(vec)
}
}