use enum_dispatch::enum_dispatch;
use nsi_sys::*;
use std::{
ffi::{c_void, CString},
marker::PhantomData,
pin::Pin,
};
use ustr::Ustr;
#[allow(unused_imports)]
use crate::*;
#[inline(always)]
pub(crate) fn get_c_param_vec(
args: Option<&ArgSlice>,
) -> (i32, *const NSIParam, Vec<NSIParam>) {
let args = match args {
Some(args) => args
.iter()
.map(|arg| NSIParam {
name: arg.name.as_char_ptr(),
data: arg.data.as_c_ptr(),
type_: arg.data.type_() as _,
arraylength: arg.array_length as _,
count: (arg.data.len() / arg.array_length) as _,
flags: arg.flags as _,
})
.collect::<Vec<_>>(),
None => Vec::new(),
};
(args.len() as _, args.as_ptr(), args)
}
pub type ArgSlice<'a, 'b> = [Arg<'a, 'b>];
pub type ArgVec<'a, 'b> = Vec<Arg<'a, 'b>>;
#[derive(Debug, Clone)]
pub struct Arg<'a, 'b> {
pub(crate) name: Ustr,
pub(crate) data: ArgData<'a, 'b>,
pub(crate) array_length: usize,
pub(crate) flags: i32,
}
impl<'a, 'b> Arg<'a, 'b> {
#[inline]
pub fn new(name: &str, data: ArgData<'a, 'b>) -> Self {
Arg {
name: Ustr::from(name),
data,
array_length: 1,
flags: 0,
}
}
#[inline]
pub fn array_len(mut self, length: usize) -> Self {
self.array_length = length;
self.flags |= NSIParamFlags::IsArray.bits();
self
}
#[inline]
pub fn per_face(mut self) -> Self {
self.flags |= NSIParamFlags::PerFace.bits();
self
}
#[inline]
pub fn per_vertex(mut self) -> Self {
self.flags |= NSIParamFlags::PerVertex.bits();
self
}
#[inline]
pub fn linear_interpolation(mut self) -> Self {
self.flags |= NSIParamFlags::InterpolateLinear.bits();
self
}
}
#[enum_dispatch(ArgData)]
pub(crate) trait ArgDataMethods {
fn type_(&self) -> Type;
fn len(&self) -> usize;
fn as_c_ptr(&self) -> *const c_void;
}
#[enum_dispatch]
#[derive(Debug, Clone)]
pub enum ArgData<'a, 'b> {
Float,
Floats(Floats<'a>),
Double,
Doubles(Doubles<'a>),
Integer,
Integers(Integers<'a>),
String(String),
Strings(Strings),
Color(Color<'a>),
Colors(Colors<'a>),
Point(Point<'a>),
Points(Points<'a>),
Vector(Vector<'a>),
Vectors(Vectors<'a>),
Normal(Normal<'a>),
Normals(Normals<'a>),
Matrix(Matrix<'a>),
Matrices(Matrices<'a>),
DoubleMatrix(DoubleMatrix<'a>),
DoubleMatrices(DoubleMatrices<'a>),
Reference(Reference<'b>),
References(References<'b>),
Callback(Callback<'b>),
}
macro_rules! nsi_data_def {
($type: ty, $name: ident, $nsi_type: expr) => {
#[derive(Debug, Clone)]
pub struct $name {
data: $type,
}
impl $name {
pub fn new(data: $type) -> Self {
Self { data }
}
}
impl ArgDataMethods for $name {
fn type_(&self) -> Type {
$nsi_type
}
fn len(&self) -> usize {
1
}
fn as_c_ptr(&self) -> *const c_void {
&self.data as *const $type as _
}
}
};
}
macro_rules! nsi_data_array_def {
($type: ty, $name: ident, $nsi_type: expr) => {
#[derive(Debug, Clone)]
pub struct $name<'a> {
data: &'a [$type],
}
impl<'a> $name<'a> {
pub fn new(data: &'a [$type]) -> Self {
debug_assert_eq!(0, data.len() % $nsi_type.elemensize());
Self { data }
}
}
impl<'a> ArgDataMethods for $name<'a> {
fn type_(&self) -> Type {
$nsi_type
}
fn len(&self) -> usize {
self.data.len() / $nsi_type.elemensize()
}
fn as_c_ptr(&self) -> *const c_void {
self.data.as_ptr() as _
}
}
};
}
macro_rules! nsi_tuple_data_def {
($type: tt, $len: expr, $name: ident, $nsi_type: expr) => {
#[derive(Debug, Clone)]
pub struct $name<'a> {
data: &'a [$type; $len],
}
impl<'a> $name<'a> {
pub fn new(data: &'a [$type; $len]) -> Self {
Self { data }
}
}
impl<'a> ArgDataMethods for $name<'a> {
fn type_(&self) -> Type {
$nsi_type
}
fn len(&self) -> usize {
1
}
fn as_c_ptr(&self) -> *const c_void {
self.data.as_ptr() as _
}
}
};
}
nsi_data_def!(f32, Float, Type::Float);
nsi_data_def!(f64, Double, Type::Double);
nsi_data_def!(i32, Integer, Type::Integer);
#[derive(Debug, Clone)]
pub struct Reference<'a> {
data: *const c_void,
_marker: PhantomData<&'a ()>,
}
unsafe impl Send for Reference<'static> {}
unsafe impl Sync for Reference<'static> {}
impl<'a> Reference<'a> {
pub fn new<T: Sized>(data: Pin<&'a T>) -> Self {
Self {
data: data.get_ref() as *const _ as _,
_marker: PhantomData,
}
}
}
impl<'a> ArgDataMethods for Reference<'a> {
fn type_(&self) -> Type {
Type::Reference
}
fn len(&self) -> usize {
1
}
fn as_c_ptr(&self) -> *const c_void {
self.data
}
}
pub trait CallbackPtr {
#[doc(hidden)]
#[allow(clippy::wrong_self_convention)]
fn to_ptr(self) -> *const c_void;
}
unsafe impl Send for Callback<'static> {}
unsafe impl Sync for Callback<'static> {}
#[derive(Debug, Clone)]
pub struct Callback<'a> {
data: *const c_void,
_marker: PhantomData<&'a mut ()>,
}
impl<'a> Callback<'a> {
pub fn new<T: CallbackPtr>(data: T) -> Self {
Self {
data: data.to_ptr(),
_marker: PhantomData,
}
}
}
impl<'a> ArgDataMethods for Callback<'a> {
fn type_(&self) -> Type {
Type::Reference
}
fn len(&self) -> usize {
1
}
fn as_c_ptr(&self) -> *const c_void {
self.data
}
}
#[derive(Debug, Clone)]
pub struct String {
#[allow(dead_code)]
data: CString,
pointer: *const c_void,
}
unsafe impl Send for String {}
unsafe impl Sync for String {}
impl String {
pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
let data = CString::new(data).unwrap();
let pointer = data.as_ptr() as _;
String { data, pointer }
}
}
impl ArgDataMethods for String {
fn type_(&self) -> Type {
Type::String
}
fn len(&self) -> usize {
1
}
fn as_c_ptr(&self) -> *const c_void {
&self.pointer as *const *const c_void as _
}
}
nsi_data_array_def!(f32, Floats, Type::Float);
nsi_data_array_def!(f64, Doubles, Type::Double);
nsi_data_array_def!(i32, Integers, Type::Integer);
nsi_data_array_def!(f32, Colors, Type::Color);
nsi_data_array_def!(f32, Points, Type::Point);
nsi_data_array_def!(f32, Vectors, Type::Vector);
nsi_data_array_def!(f32, Normals, Type::Normal);
nsi_data_array_def!(f32, Matrices, Type::Matrix);
nsi_data_array_def!(f64, DoubleMatrices, Type::DoubleMatrix);
#[derive(Debug, Clone)]
pub struct References<'a> {
data: Vec<*const c_void>,
_marker: PhantomData<&'a ()>,
}
unsafe impl Send for References<'static> {}
unsafe impl Sync for References<'static> {}
impl<'a> References<'a> {
pub fn new<T>(data: &'a [&'a T]) -> Self {
debug_assert_eq!(0, data.len() % Type::Reference.elemensize());
Self {
data: data.iter().map(|r| r as *const _ as _).collect(),
_marker: PhantomData,
}
}
}
impl<'a> ArgDataMethods for References<'a> {
fn type_(&self) -> Type {
Type::Reference
}
fn len(&self) -> usize {
self.data.len() / Type::Reference.elemensize()
}
fn as_c_ptr(&self) -> *const c_void {
self.data.as_ptr() as _
}
}
#[derive(Debug, Clone)]
pub struct Strings {
#[allow(dead_code)]
data: Vec<CString>,
pointer: Vec<*const c_void>,
}
unsafe impl Send for Strings {}
unsafe impl Sync for Strings {}
impl Strings {
pub fn new<T: Into<Vec<u8>> + Copy>(data: &[T]) -> Self {
let data = data
.iter()
.map(|s| CString::new(*s).unwrap())
.collect::<Vec<_>>();
let pointer = data.iter().map(|s| s.as_ptr() as _).collect();
Strings { data, pointer }
}
}
impl ArgDataMethods for Strings {
fn type_(&self) -> Type {
Type::String
}
fn len(&self) -> usize {
self.pointer.len()
}
fn as_c_ptr(&self) -> *const c_void {
self.pointer.as_ptr() as _
}
}
nsi_tuple_data_def!(f32, 3, Color, Type::Color);
nsi_tuple_data_def!(f32, 3, Point, Type::Point);
nsi_tuple_data_def!(f32, 3, Vector, Type::Vector);
nsi_tuple_data_def!(f32, 3, Normal, Type::Normal);
nsi_tuple_data_def!(f32, 16, Matrix, Type::Matrix);
nsi_tuple_data_def!(f64, 16, DoubleMatrix, Type::DoubleMatrix);
#[derive(Copy, Clone, Debug, PartialEq)]
#[repr(i32)]
pub(crate) enum Type {
Float = NSIType::Float as _,
Double = NSIType::Double as _,
Integer = NSIType::Integer as _,
String = NSIType::String as _,
Color = NSIType::Color as _,
Point = NSIType::Point as _,
Vector = NSIType::Vector as _,
Normal = NSIType::Normal as _,
Matrix = NSIType::Matrix as _,
DoubleMatrix = NSIType::DoubleMatrix as _,
Reference = NSIType::Pointer as _,
}
impl Type {
#[inline]
pub(crate) fn elemensize(&self) -> usize {
match self {
Type::Float => 1,
Type::Double => 1,
Type::Integer => 1,
Type::String => 1,
Type::Color => 3,
Type::Point => 3,
Type::Vector => 3,
Type::Normal => 3,
Type::Matrix => 16,
Type::DoubleMatrix => 16,
Type::Reference => 1,
}
}
}
#[macro_export]
macro_rules! float {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Float::new($value)))
};
}
#[macro_export]
macro_rules! floats {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Floats::new($value)))
};
}
#[macro_export]
macro_rules! double {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Double::new($value)))
};
}
#[macro_export]
macro_rules! doubles {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Doubles::new($value)))
};
}
#[macro_export]
macro_rules! integer {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Integer::new($value)))
};
}
#[macro_export]
macro_rules! integers {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Integers::new($value)))
};
}
#[macro_export]
macro_rules! color {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Color::new($value)))
};
}
#[macro_export]
macro_rules! colors {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Colors::new($value)))
};
}
#[macro_export]
macro_rules! point {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Point::new($value)))
};
}
#[macro_export]
macro_rules! points {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Points::new($value)))
};
}
#[macro_export]
macro_rules! vector {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Vector::new($value)))
};
}
#[macro_export]
macro_rules! vectors {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Vectors::new($value)))
};
}
#[macro_export]
macro_rules! normal {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Normal::new($value)))
};
}
#[macro_export]
macro_rules! normals {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Normals::new($value)))
};
}
#[macro_export]
macro_rules! matrix {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Matrix::new($value)))
};
}
#[macro_export]
macro_rules! matrices {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Matrices::new($value)))
};
}
#[macro_export]
macro_rules! double_matrix {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::DoubleMatrix::new($value)))
};
}
#[macro_export]
macro_rules! double_matrices {
($name: tt, $value: expr) => {
nsi::Arg::new(
$name,
nsi::ArgData::from(nsi::DoubleMatrices::new($value)),
)
};
}
#[macro_export]
macro_rules! string {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::String::new($value)))
};
}
#[macro_export]
macro_rules! strings {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Strings::new($value)))
};
}
#[macro_export]
macro_rules! reference {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Reference::new($value)))
};
}
#[macro_export]
macro_rules! references {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::References::new($value)))
};
}
#[macro_export]
macro_rules! callback {
($name: tt, $value: expr) => {
nsi::Arg::new($name, nsi::ArgData::from(nsi::Callback::new($value)))
};
}