use std::ffi::c_void;
use anyhow::anyhow;
use cid::Cid;
use noosphere_core::context::{HasSphereContext, SphereSync};
use noosphere_core::data::Mnemonic;
use noosphere_core::{authority::Authorization, data::Did};
use safer_ffi::char_p::InvalidNulTerminator;
use safer_ffi::prelude::*;
use crate::ffi::{NsError, NsNoosphere, NsSphere, TryOrInitialize};
use crate::sphere::SphereReceipt;
#[derive_ReprC(rename = "ns_sphere_receipt")]
#[repr(opaque)]
pub struct NsSphereReceipt {
inner: SphereReceipt,
}
impl From<SphereReceipt> for NsSphereReceipt {
fn from(inner: SphereReceipt) -> Self {
NsSphereReceipt { inner }
}
}
#[ffi_export]
pub fn ns_sphere_receipt_identity(
sphere_receipt: &NsSphereReceipt,
error_out: Option<Out<'_, repr_c::Box<NsError>>>,
) -> Option<char_p::Box> {
error_out.try_or_initialize(|| {
sphere_receipt
.inner
.identity
.to_string()
.try_into()
.map_err(|error: InvalidNulTerminator<String>| anyhow!(error).into())
})
}
#[ffi_export]
pub fn ns_sphere_receipt_mnemonic(
sphere_receipt: &NsSphereReceipt,
error_out: Option<Out<'_, repr_c::Box<NsError>>>,
) -> Option<char_p::Box> {
error_out.try_or_initialize(|| {
sphere_receipt
.inner
.mnemonic
.to_string()
.try_into()
.map_err(|error: InvalidNulTerminator<String>| anyhow!(error).into())
})
}
#[ffi_export]
pub fn ns_sphere_receipt_free(sphere_receipt: repr_c::Box<NsSphereReceipt>) {
drop(sphere_receipt)
}
#[ffi_export]
pub fn ns_sphere_create(
noosphere: &mut NsNoosphere,
owner_key_name: char_p::Ref<'_>,
error_out: Option<Out<'_, repr_c::Box<NsError>>>,
) -> Option<repr_c::Box<NsSphereReceipt>> {
error_out.try_or_initialize(|| {
Ok(Box::<NsSphereReceipt>::new(
noosphere
.async_runtime()
.block_on(noosphere.inner().create_sphere(owner_key_name.to_str()))
.map(|receipt| receipt.into())?,
)
.into())
})
}
#[ffi_export]
pub fn ns_sphere_join(
noosphere: &mut NsNoosphere,
sphere_identity: char_p::Ref<'_>,
local_key_name: char_p::Ref<'_>,
authorization: char_p::Ref<'_>,
error_out: Option<Out<'_, repr_c::Box<NsError>>>,
) {
error_out.try_or_initialize(|| {
let authorization = Authorization::Cid(
Cid::try_from(authorization.to_str()).map_err(|error| anyhow!(error))?,
);
noosphere
.async_runtime()
.block_on(noosphere.inner().join_sphere(
&Did::from(sphere_identity.to_str()),
local_key_name.to_str(),
Some(&authorization),
))
.map_err(|error| error.into())
});
}
#[ffi_export]
pub fn ns_sphere_version_get(
noosphere: &NsNoosphere,
sphere_identity: char_p::Ref<'_>,
error_out: Option<Out<'_, repr_c::Box<NsError>>>,
) -> Option<char_p::Box> {
error_out.try_or_initialize(|| {
noosphere.async_runtime().block_on(async {
let sphere_channel = noosphere
.inner()
.get_sphere_channel(&Did(sphere_identity.to_str().into()))
.await?;
let sphere_context = sphere_channel.immutable();
sphere_context
.to_sphere()
.await?
.cid()
.to_string()
.try_into()
.map_err(|error: InvalidNulTerminator<String>| anyhow!(error).into())
})
})
}
#[ffi_export]
pub fn ns_sphere_sync(
noosphere: &NsNoosphere,
sphere: &NsSphere,
context: Option<repr_c::Box<c_void>>,
callback: extern "C" fn(
Option<repr_c::Box<c_void>>,
Option<repr_c::Box<NsError>>,
Option<char_p::Box>,
),
) {
let mut sphere_channel = sphere.to_channel();
noosphere.async_runtime().spawn(async move {
let result: Result<char_p::Box, anyhow::Error> = async {
sphere_channel.mutable().sync().await?;
sphere_channel
.immutable()
.to_sphere()
.await?
.cid()
.to_string()
.try_into()
.map_err(|error: InvalidNulTerminator<String>| anyhow!(error))
}
.await;
match result {
Ok(cid_string) => {
tokio::task::spawn_blocking(move || callback(context, None, Some(cid_string)))
}
Err(error) => tokio::task::spawn_blocking(move || {
callback(context, Some(NsError::from(error).into()), None)
}),
};
});
}
#[ffi_export]
pub fn ns_sphere_sync_blocking(
noosphere: &NsNoosphere,
sphere_identity: char_p::Ref<'_>,
error_out: Option<Out<'_, repr_c::Box<NsError>>>,
) -> Option<char_p::Box> {
error_out.try_or_initialize(|| {
let cid = noosphere.async_runtime().block_on(async {
let mut sphere_channel = noosphere
.inner()
.get_sphere_channel(&Did(sphere_identity.to_str().into()))
.await?;
sphere_channel.mutable().sync().await?;
Ok(sphere_channel
.immutable()
.to_sphere()
.await?
.cid()
.to_string()) as Result<String, anyhow::Error>
})?;
Ok(cid
.try_into()
.map_err(|error: InvalidNulTerminator<String>| anyhow!(error))?)
})
}
#[ffi_export]
pub fn ns_sphere_recover(
noosphere: &NsNoosphere,
sphere_identity: char_p::Ref<'_>,
local_key_name: char_p::Ref<'_>,
mnemonic: char_p::Ref<'_>,
context: Option<repr_c::Box<c_void>>,
callback: extern "C" fn(Option<repr_c::Box<c_void>>, Option<repr_c::Box<NsError>>),
) {
let noosphere_inner = noosphere.inner().clone();
let sphere_identity = Did(sphere_identity.to_string());
let mnemonic = Mnemonic(mnemonic.to_string());
let local_key_name = local_key_name.to_string();
noosphere.async_runtime().spawn(async move {
let result = noosphere_inner
.recover_sphere(&local_key_name, &sphere_identity, &mnemonic)
.await;
match result {
Ok(_) => tokio::task::spawn_blocking(move || callback(context, None)),
Err(error) => tokio::task::spawn_blocking(move || {
callback(context, Some(NsError::from(error).into()))
}),
};
});
}