use bitflags::bitflags;
#[repr(i32)]
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
pub enum Type {
Invalid = 0,
F32 = 1,
F64 = 0x11,
I32 = 2,
I64 = 0x12,
String = 3,
Color = 4,
Point = 5,
Vector = 6,
Normal = 7,
MatrixF32 = 8,
MatrixF64 = 0x18,
Reference = 9,
}
bitflags! {
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub struct Flags: i32 {
const IS_ARRAY = 1;
const PER_FACE = 2;
const PER_VERTEX = 4;
const INTERPOLATE_LINEAR = 8;
}
}
#[repr(C)]
#[derive(Debug, Clone, Copy)]
pub struct FfiParam {
pub name: *const core::ffi::c_char,
pub data: *const core::ffi::c_void,
pub type_: core::ffi::c_int,
pub arraylength: core::ffi::c_int,
pub count: usize,
pub flags: core::ffi::c_int,
}
unsafe impl Send for FfiParam {}
unsafe impl Sync for FfiParam {}
#[cfg(feature = "ustr")]
pub type Name = ustr::Ustr;
#[cfg(not(feature = "ustr"))]
pub type Name = String;
pub trait ParamValue {
fn name(&self) -> &str;
fn type_tag(&self) -> Type;
fn len(&self) -> usize;
fn is_empty(&self) -> bool {
self.len() == 0
}
fn array_length(&self) -> usize {
1
}
fn flags(&self) -> i32 {
0
}
fn as_c_param(&self) -> Option<FfiParam>;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Action {
Start,
Wait,
Synchronize,
Suspend,
Resume,
Stop,
}
impl Action {
#[inline]
pub const fn as_str(&self) -> &'static str {
match self {
Self::Start => "start",
Self::Wait => "wait",
Self::Synchronize => "synchronize",
Self::Suspend => "suspend",
Self::Resume => "resume",
Self::Stop => "stop",
}
}
pub fn from_name(s: &str) -> Option<Self> {
Some(match s {
"start" => Self::Start,
"wait" => Self::Wait,
"synchronize" => Self::Synchronize,
"suspend" => Self::Suspend,
"resume" => Self::Resume,
"stop" => Self::Stop,
_ => return None,
})
}
}
impl std::fmt::Display for Action {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
f.write_str(self.as_str())
}
}
pub trait Nsi: Send + Sync {
type Arg<'call>: ParamValue
where
Self: 'call;
type Error: std::error::Error + Send + Sync + 'static;
fn create(
&self,
handle: &str,
node_type: &str,
args: Option<&[Self::Arg<'_>]>,
) -> Result<(), Self::Error>;
fn delete(
&self,
handle: &str,
args: Option<&[Self::Arg<'_>]>,
) -> Result<(), Self::Error>;
fn set_attribute(
&self,
handle: &str,
args: &[Self::Arg<'_>],
) -> Result<(), Self::Error>;
fn set_attribute_at_time(
&self,
handle: &str,
time: f64,
args: &[Self::Arg<'_>],
) -> Result<(), Self::Error>;
fn delete_attribute(
&self,
handle: &str,
name: &str,
) -> Result<(), Self::Error>;
fn connect(
&self,
from: &str,
from_attr: Option<&str>,
to: &str,
to_attr: &str,
args: Option<&[Self::Arg<'_>]>,
) -> Result<(), Self::Error>;
fn disconnect(
&self,
from: &str,
from_attr: Option<&str>,
to: &str,
to_attr: &str,
) -> Result<(), Self::Error>;
fn evaluate(&self, args: &[Self::Arg<'_>]) -> Result<(), Self::Error>;
fn render_control(
&self,
action: Action,
args: Option<&[Self::Arg<'_>]>,
) -> Result<(), Self::Error>;
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn action_round_trip() {
let actions = [
Action::Start,
Action::Wait,
Action::Synchronize,
Action::Suspend,
Action::Resume,
Action::Stop,
];
for action in actions {
let s = action.as_str();
let parsed = Action::from_name(s).expect("should parse");
assert_eq!(action, parsed);
}
}
#[test]
fn type_values_match_c_header() {
assert_eq!(Type::Invalid as i32, 0);
assert_eq!(Type::F32 as i32, 1);
assert_eq!(Type::F64 as i32, 0x11);
assert_eq!(Type::I32 as i32, 2);
assert_eq!(Type::I64 as i32, 0x12);
assert_eq!(Type::String as i32, 3);
assert_eq!(Type::Color as i32, 4);
assert_eq!(Type::Point as i32, 5);
assert_eq!(Type::Vector as i32, 6);
assert_eq!(Type::Normal as i32, 7);
assert_eq!(Type::MatrixF32 as i32, 8);
assert_eq!(Type::MatrixF64 as i32, 0x18);
assert_eq!(Type::Reference as i32, 9);
}
#[test]
fn flags_combine() {
let f = Flags::PER_VERTEX | Flags::IS_ARRAY;
assert!(f.contains(Flags::PER_VERTEX));
assert!(f.contains(Flags::IS_ARRAY));
assert!(!f.contains(Flags::PER_FACE));
assert_eq!(f.bits(), 5);
}
#[test]
fn ffi_param_layout() {
assert!(std::mem::size_of::<FfiParam>() >= 6 * 4);
}
}