use crate::{Arg, ArgSlice};
use ::nsi_trait::{Action, NodeType, Nsi};
use std::{
collections::HashMap,
ffi::c_int,
sync::{
Arc, Mutex,
atomic::{AtomicI32, Ordering},
},
};
pub struct FfiApiAdapter<T>
where
T: Nsi + 'static,
for<'a> T: Nsi<Arg<'a> = Arg<'a, 'a>>,
{
factory: Box<dyn Fn() -> T + Send + Sync>,
contexts: Mutex<HashMap<c_int, Arc<T>>>,
next_id: AtomicI32,
}
impl<T> FfiApiAdapter<T>
where
T: Nsi + 'static,
for<'a> T: Nsi<Arg<'a> = Arg<'a, 'a>>,
{
pub fn new<F>(factory: F) -> Self
where
F: Fn() -> T + Send + Sync + 'static,
{
Self {
factory: Box::new(factory),
contexts: Mutex::new(HashMap::new()),
next_id: AtomicI32::new(1),
}
}
#[inline]
fn lookup(&self, ctx: c_int) -> Option<Arc<T>> {
self.contexts.lock().ok()?.get(&ctx).cloned()
}
pub fn begin(&self, _args: Option<&ArgSlice>) -> c_int {
let nsi = (self.factory)();
let id = self.next_id.fetch_add(1, Ordering::SeqCst);
if let Ok(mut contexts) = self.contexts.lock() {
contexts.insert(id, Arc::new(nsi));
id
} else {
0
}
}
pub fn end(&self, ctx: c_int) {
if let Ok(mut contexts) = self.contexts.lock() {
contexts.remove(&ctx);
}
}
pub fn create(
&self,
ctx: c_int,
handle: &str,
node_type: NodeType,
args: Option<&ArgSlice>,
) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.create(handle, node_type.as_str(), args);
}
}
pub fn delete(&self, ctx: c_int, handle: &str, args: Option<&ArgSlice>) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.delete(handle, args);
}
}
pub fn set_attribute(&self, ctx: c_int, handle: &str, args: &ArgSlice) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.set_attribute(handle, args);
}
}
pub fn set_attribute_at_time(
&self,
ctx: c_int,
handle: &str,
time: f64,
args: &ArgSlice,
) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.set_attribute_at_time(handle, time, args);
}
}
pub fn delete_attribute(&self, ctx: c_int, handle: &str, name: &str) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.delete_attribute(handle, name);
}
}
pub fn connect(
&self,
ctx: c_int,
from: &str,
from_attr: Option<&str>,
to: &str,
to_attr: &str,
args: Option<&ArgSlice>,
) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.connect(from, from_attr, to, to_attr, args);
}
}
pub fn disconnect(
&self,
ctx: c_int,
from: &str,
from_attr: Option<&str>,
to: &str,
to_attr: &str,
) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.disconnect(from, from_attr, to, to_attr);
}
}
pub fn evaluate(&self, ctx: c_int, args: Option<&ArgSlice>) {
if let Some(nsi) = self.lookup(ctx) {
let empty: &[Arg<'_, '_>] = &[];
let a = args.unwrap_or(empty);
let _ = nsi.evaluate(a);
}
}
pub fn render_control(
&self,
ctx: c_int,
action: Action,
args: Option<&ArgSlice>,
) {
if let Some(nsi) = self.lookup(ctx) {
let _ = nsi.render_control(action, args);
}
}
}
unsafe impl<T> Send for FfiApiAdapter<T>
where
T: Nsi + 'static,
for<'a> T: Nsi<Arg<'a> = Arg<'a, 'a>>,
{
}
unsafe impl<T> Sync for FfiApiAdapter<T>
where
T: Nsi + 'static,
for<'a> T: Nsi<Arg<'a> = Arg<'a, 'a>>,
{
}