use anyhow::{Result, anyhow};
use pyo3::prelude::*;
use pyo3::types::{IntoPyDict, PyList};
use crate::utils::{ShortHash, StdErrLog, StdOutLog};
pub struct PyRNSIdentity(pub(crate) PyObject);
pub struct RNSDestination(pub(crate) PyRNSDestination);
pub(crate) struct PyRNSDestination(pub(crate) PyObject);
pub(crate) struct PyRNSDestinationHash(pub(crate) ShortHash);
pub struct PyRNS {
pub(crate) identity: PyRNSIdentity,
rns_mod: Py<PyModule>,
}
#[derive(Debug)]
pub struct PrivateKey(Vec<u8>);
impl PrivateKey {
pub fn bytes(&self) -> &[u8] {
&self.0
}
pub fn from_bytes(bytes: Vec<u8>) -> Self {
Self(bytes)
}
}
impl PyRNS {
pub fn initialize(identity_key: Option<PrivateKey>) -> Result<Self> {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let sys = py.import("sys")?;
sys.setattr("stdout", StdOutLog.into_pyobject(py)?)?;
sys.setattr("stderr", StdErrLog.into_pyobject(py)?)?;
let rns_mod = PyModule::import(py, "RNS")?;
let _ = rns_mod.call_method0("Reticulum")?;
let identity = if let Some(private_key) = identity_key {
let kwargs = [("create_keys", false)].into_py_dict(py)?;
let id = rns_mod.call_method("Identity", (), Some(&kwargs))?;
let r: bool = id
.call_method1("load_private_key", (private_key.0,))?
.extract()?;
if !r {
return Err(anyhow!("Failed to load private key"));
}
id
} else {
rns_mod.call_method0("Identity")?
};
let identity = PyRNSIdentity(identity.unbind());
Ok(Self {
identity,
rns_mod: rns_mod.unbind(),
})
})
}
pub fn generate_private_key() -> Result<PrivateKey> {
pyo3::prepare_freethreaded_python();
Python::with_gil(|py| {
let rns_mod = PyModule::import(py, "RNS")?;
let identity = rns_mod.call_method0("Identity")?;
let key: Vec<u8> = identity.call_method0("get_private_key")?.extract()?;
Ok(PrivateKey(key))
})
}
pub fn identity(&self) -> &PyRNSIdentity {
&self.identity
}
pub fn get_private_key(&self) -> Result<PrivateKey> {
Python::with_gil(|py| {
let key = self.identity.0.bind(py).call_method0("get_private_key")?;
let key: Vec<u8> = key.extract()?;
Ok(PrivateKey(key))
})
}
pub fn transport_has_path(&self, hash: &ShortHash) -> Result<bool> {
Python::with_gil(|py| {
let has_path: bool = self
.rns_mod
.getattr(py, "Transport")?
.call_method1(py, "has_path", (hash.value(),))?
.extract(py)?;
Ok(has_path)
})
}
pub fn transport_request_path(&self, hash: &ShortHash) -> Result<()> {
Python::with_gil(|py| {
self.rns_mod.getattr(py, "Transport")?.call_method1(
py,
"request_path",
(hash.value(),),
)?;
Ok(())
})
}
pub fn identity_recall(&self, hash: &ShortHash) -> Result<PyRNSIdentity> {
Python::with_gil(|py| {
let id = self.rns_mod.getattr(py, "Identity")?.call_method1(
py,
"recall",
(hash.value(),),
)?;
Ok(PyRNSIdentity(id))
})
}
pub fn identity_recall_app_data(&self, hash: &ShortHash) -> Result<Option<Vec<u8>>> {
Python::with_gil(|py| {
let app_data: Option<Vec<u8>> = self
.rns_mod
.getattr(py, "Identity")?
.call_method1(py, "recall_app_data", (hash.value(),))?
.extract(py)?;
Ok(app_data)
})
}
pub fn create_destination(&self, recipient_identity: &PyRNSIdentity) -> Result<RNSDestination> {
Python::with_gil(|py| {
let args = PyList::empty(py);
args.append(&recipient_identity.0)?;
args.append(DestinationDirections::OUT as isize)?;
args.append(DestinationTypes::SINGLE as isize)?;
args.append("lxmf")?;
args.append("delivery")?;
let destination = self
.rns_mod
.call_method(py, "Destination", args.to_tuple(), None)?;
Ok(RNSDestination(PyRNSDestination(destination)))
})
}
}
pub enum DestinationTypes {
SINGLE = 0x00,
GROUP = 0x01,
PLAIN = 0x02,
LINK = 0x03,
}
pub enum DestinationDirections {
IN = 0x11,
OUT = 0x12,
}
#[cfg(test)]
mod tests {
use crate::rns::{DestinationDirections, DestinationTypes};
#[test]
fn enum_discrimnants_work() {
let single = DestinationTypes::SINGLE;
assert_eq!(0, single as isize);
let plain = DestinationTypes::PLAIN;
assert_eq!(2, plain as isize);
let out = DestinationDirections::OUT;
assert_eq!(18, out as isize);
}
}