querent_synapse/cross/
clrepr_python.rs

1use crate::cross::{clrepr::CLReprObject, CLRepr, StringType};
2use pyo3::{
3	exceptions::{PyNotImplementedError, PyTypeError},
4	types::{
5		PyBool, PyComplex, PyDate, PyDict, PyFloat, PyFrame, PyFunction, PyInt, PyList, PySequence,
6		PySet, PyString, PyTraceback, PyTuple,
7	},
8	Py, PyAny, PyErr, PyObject, Python, ToPyObject,
9};
10
11#[derive(Debug, Clone)]
12pub enum PythonRef {
13	PyObject(PyObject),
14	PyFunction(Py<PyFunction>),
15	/// Special type to transfer functions through JavaScript
16	/// In JS it's an external object. It's not the same as Function.
17	PyExternalFunction(Py<PyFunction>),
18}
19
20pub trait CLReprPython: Sized {
21	fn from_python_ref(v: &PyAny) -> Result<Self, PyErr>;
22	fn into_py_impl(from: CLRepr, py: Python) -> Result<PyObject, PyErr>;
23	fn into_py(self, py: Python) -> Result<PyObject, PyErr>;
24}
25
26impl CLReprPython for CLRepr {
27	/// Convert python value to CLRepr
28	fn from_python_ref(v: &PyAny) -> Result<Self, PyErr> {
29		if v.is_none() {
30			return Ok(Self::Null);
31		}
32
33		return Ok(if v.get_type().is_subclass_of::<PyString>()? {
34			let string_type =
35				if v.hasattr("is_safe")? { StringType::Safe } else { StringType::Normal };
36
37			Self::String(v.to_string(), string_type)
38		} else if v.get_type().is_subclass_of::<PyBool>()? {
39			Self::Bool(v.downcast::<PyBool>()?.is_true())
40		} else if v.get_type().is_subclass_of::<PyFloat>()? {
41			let f = v.downcast::<PyFloat>()?;
42			Self::Float(f.value())
43		} else if v.get_type().is_subclass_of::<PyInt>()? {
44			let i: i64 = v.downcast::<PyInt>()?.extract()?;
45			Self::Int(i)
46		} else if v.get_type().is_subclass_of::<PyDict>()? {
47			let d = v.downcast::<PyDict>()?;
48			let mut obj = CLReprObject::new();
49
50			for (k, v) in d.iter() {
51				if k.get_type().is_subclass_of::<PyString>()? {
52					let key_str = k.downcast::<PyString>()?;
53
54					obj.insert(key_str.to_string(), Self::from_python_ref(v)?);
55				}
56			}
57
58			Self::Object(obj)
59		} else if v.get_type().is_subclass_of::<PyList>()? {
60			let l = v.downcast::<PyList>()?;
61			let mut r = Vec::with_capacity(l.len());
62
63			for v in l.iter() {
64				r.push(Self::from_python_ref(v)?);
65			}
66
67			Self::Array(r)
68		} else if v.get_type().is_subclass_of::<PySet>()? {
69			let l = v.downcast::<PySet>()?;
70			let mut r = Vec::with_capacity(l.len());
71
72			for v in l.iter() {
73				r.push(Self::from_python_ref(v)?);
74			}
75
76			Self::Array(r)
77		} else if v.get_type().is_subclass_of::<PyTuple>()? {
78			let l = v.downcast::<PyTuple>()?;
79			let mut r = Vec::with_capacity(l.len());
80
81			for v in l.iter() {
82				r.push(Self::from_python_ref(v)?);
83			}
84
85			Self::Tuple(r)
86		} else if v.get_type().is_subclass_of::<PyFunction>()? {
87			let fun: Py<PyFunction> = v.downcast::<PyFunction>()?.into();
88
89			Self::PythonRef(PythonRef::PyFunction(fun))
90		} else if v.get_type().is_subclass_of::<PyComplex>()? {
91			return Err(PyErr::new::<PyTypeError, _>(
92				"Unable to represent PyComplex type as CLR from Python".to_string(),
93			));
94		} else if v.get_type().is_subclass_of::<PyDate>()? {
95			return Err(PyErr::new::<PyTypeError, _>(
96				"Unable to represent PyDate type as CLR from Python".to_string(),
97			));
98		} else if v.get_type().is_subclass_of::<PyFrame>()? {
99			let frame = v.downcast::<PyFrame>()?;
100
101			return Err(PyErr::new::<PyTypeError, _>(format!(
102				"Unable to represent PyFrame type as CLR from Python, value: {:?}",
103				frame
104			)));
105		} else if v.get_type().is_subclass_of::<PyTraceback>()? {
106			let trb = v.downcast::<PyTraceback>()?;
107
108			return Err(PyErr::new::<PyTypeError, _>(format!(
109				"Unable to represent PyTraceback type as CLR from Python, value: {:?}",
110				trb
111			)));
112		} else {
113			let is_sequence = unsafe { pyo3::ffi::PySequence_Check(v.as_ptr()) == 1 };
114			if is_sequence {
115				let seq = v.downcast::<PySequence>()?;
116
117				return Err(PyErr::new::<PyTypeError, _>(format!(
118					"Unable to represent PySequence type as CLR from Python, value: {:?}",
119					seq
120				)));
121			}
122
123			Self::PythonRef(PythonRef::PyObject(v.into()))
124		});
125	}
126
127	fn into_py_impl(from: CLRepr, py: Python) -> Result<PyObject, PyErr> {
128		Ok(match from {
129			CLRepr::String(v, _) => PyString::new(py, &v).to_object(py),
130			CLRepr::Bool(v) => PyBool::new(py, v).to_object(py),
131			CLRepr::Float(v) => PyFloat::new(py, v).to_object(py),
132			CLRepr::Int(v) => {
133				let py_int: &PyInt = unsafe { py.from_owned_ptr(pyo3::ffi::PyLong_FromLong(v)) };
134
135				py_int.to_object(py)
136			},
137			CLRepr::Array(arr) => {
138				let mut elements = Vec::with_capacity(arr.len());
139
140				for el in arr.into_iter() {
141					elements.push(Self::into_py_impl(el, py)?);
142				}
143
144				PyList::new(py, elements).to_object(py)
145			},
146			CLRepr::Tuple(arr) => {
147				let mut elements = Vec::with_capacity(arr.len());
148
149				for el in arr.into_iter() {
150					elements.push(Self::into_py_impl(el, py)?);
151				}
152
153				PyTuple::new(py, elements).to_object(py)
154			},
155			CLRepr::Object(obj) => {
156				let r = PyDict::new(py);
157
158				for (k, v) in obj.into_iter() {
159					r.set_item(k, Self::into_py_impl(v, py)?)?;
160				}
161
162				r.to_object(py)
163			},
164			CLRepr::Null => py.None(),
165			CLRepr::PythonRef(py_ref) => match py_ref {
166				PythonRef::PyObject(_) =>
167					return Err(PyErr::new::<PyNotImplementedError, _>(
168						"Unable to represent PyObject in Python",
169					)),
170				PythonRef::PyFunction(_) =>
171					return Err(PyErr::new::<PyNotImplementedError, _>(
172						"Unable to represent PyFunction in Python",
173					)),
174				PythonRef::PyExternalFunction(_) =>
175					return Err(PyErr::new::<PyNotImplementedError, _>(
176						"Unable to represent PyExternalFunction in Python",
177					)),
178			},
179		})
180	}
181
182	fn into_py(self, py: Python) -> Result<PyObject, PyErr> {
183		Self::into_py_impl(self, py)
184	}
185}