nsi_core/
argument.rs

1//! Optional arguments passed to methods of an ɴsɪ context.
2use enum_dispatch::enum_dispatch;
3use nsi_sys::*;
4use std::{
5    ffi::{c_void, CString},
6    marker::PhantomData,
7    pin::Pin,
8};
9use ustr::Ustr;
10
11// Needed for docs.
12#[allow(unused_imports)]
13use crate::*;
14
15#[inline(always)]
16pub(crate) fn get_c_param_vec(
17    args: Option<&ArgSlice>,
18) -> (i32, *const NSIParam, Vec<NSIParam>) {
19    let args = match args {
20        Some(args) => args
21            .iter()
22            .map(|arg| NSIParam {
23                name: arg.name.as_char_ptr(),
24                data: arg.data.as_c_ptr(),
25                type_: arg.data.type_() as _,
26                arraylength: arg.array_length as _,
27                count: (arg.data.len() / arg.array_length) as _,
28                flags: arg.flags as _,
29            })
30            .collect::<Vec<_>>(),
31        None => Vec::new(),
32    };
33
34    (args.len() as _, args.as_ptr(), args)
35}
36
37/// A slice of (optional) arguments passed to a method of
38/// [`Context`].
39pub type ArgSlice<'a, 'b> = [Arg<'a, 'b>];
40
41/// A vector of (optional) arguments passed to a method of
42/// [`Context`].
43pub type ArgVec<'a, 'b> = Vec<Arg<'a, 'b>>;
44
45/// An (optional) argument passed to a method of
46/// [`Context`].
47#[derive(Debug, Clone)]
48pub struct Arg<'a, 'b> {
49    pub(crate) name: Ustr,
50    pub(crate) data: ArgData<'a, 'b>,
51    // length of each element if an array type
52    pub(crate) array_length: usize,
53    // number of elements
54    pub(crate) flags: i32,
55}
56
57impl<'a, 'b> Arg<'a, 'b> {
58    #[inline]
59    pub fn new(name: &str, data: ArgData<'a, 'b>) -> Self {
60        Arg {
61            name: Ustr::from(name),
62            data,
63            array_length: 1,
64            flags: 0,
65        }
66    }
67
68    /// Sets the length of the argument for each element.
69    #[inline]
70    pub fn array_len(mut self, length: usize) -> Self {
71        self.array_length = length;
72        self.flags |= NSIParamFlags::IsArray.bits();
73        self
74    }
75
76    /// Marks this argument as having per-face granularity.
77    #[inline]
78    pub fn per_face(mut self) -> Self {
79        self.flags |= NSIParamFlags::PerFace.bits();
80        self
81    }
82
83    /// Marks this argument as having per-vertex granularity.
84    #[inline]
85    pub fn per_vertex(mut self) -> Self {
86        self.flags |= NSIParamFlags::PerVertex.bits();
87        self
88    }
89
90    /// Marks this argument as to be interpolated linearly.
91    #[inline]
92    pub fn linear_interpolation(mut self) -> Self {
93        self.flags |= NSIParamFlags::InterpolateLinear.bits();
94        self
95    }
96}
97
98#[enum_dispatch(ArgData)]
99pub(crate) trait ArgDataMethods {
100    //const TYPE: Type;
101    fn type_(&self) -> Type;
102    fn len(&self) -> usize;
103    fn as_c_ptr(&self) -> *const c_void;
104}
105
106/// A variant describing data passed to the renderer.
107///
108/// # Lifetimes
109/// Lifetime `'a` is for any tuple or array type as these are
110/// passed as references and only need to live as long as the
111/// function call where they get passed.
112///
113/// Lifetime `'b` is for the arbitrary reference type. This is
114/// pegged to the lifetime of the [`Context`](crate::context::Context).
115/// Use this to pass arbitrary Rust data through the FFI boundary.
116#[enum_dispatch]
117#[derive(Debug, Clone)]
118pub enum ArgData<'a, 'b> {
119    /// Single [`f32`] value.
120    Float,
121    /// An `[`[`f32`]`]` slice.
122    Floats(Floats<'a>),
123    /// Single [`f64`] value.
124    Double,
125    /// An `[`[`f64`]`]` slice.
126    Doubles(Doubles<'a>),
127    /// Single [`i32`] value.
128    Integer,
129    /// An `[`[`i32`]`]` slice.
130    Integers(Integers<'a>),
131    /// A [`String`].
132    String(String),
133    /// A `[`[`String`]`]` slice.
134    Strings(Strings),
135    /// Color in linear space, given as a red, green, blue triplet
136    /// of [`f32`] values; usually in the range `0..1`.
137    Color(Color<'a>),
138    /// A flat `[`[`f32`]`]` slice of colors (`len % 3 == 0`).
139    Colors(Colors<'a>),
140    /// Point, given as three [`f32`] values.
141    Point(Point<'a>),
142    /// A flat `[`[`f32`]`]` slice of points (`len % 3 == 0`).
143    Points(Points<'a>),
144    /// Vector, given as three [`f32`] values.
145    Vector(Vector<'a>),
146    /// A flat `[`[`f32`]`]` slice of vectors (`len % 3 == 0`).
147    Vectors(Vectors<'a>),
148    /// Normal vector, given as three [`f32`] values.
149    Normal(Normal<'a>),
150    /// A flat `[`[`f32`]`]` slice of normals (`len % 3 == 0`).
151    Normals(Normals<'a>),
152    /// Row-major, 4×4 transformation matrix, given as 16 [`f32`] values.
153    Matrix(Matrix<'a>),
154    /// A flat `[`[`f32`]`]` slice of matrices (`len % 16 == 0`).
155    Matrices(Matrices<'a>),
156    /// Row-major, 4×4 transformation matrix, given as 16 [`f64`] values.
157    DoubleMatrix(DoubleMatrix<'a>),
158    /// A flat `[`[`f64`]`]` slice of matrices (`len % 16 == 0`).
159    DoubleMatrices(DoubleMatrices<'a>),
160    /// Reference *with* lifetime guarantees.
161    ///
162    /// This gets converted to a raw pointer when passed
163    /// through the FFI boundary.
164    ///
165    /// ```
166    /// # use nsi_core as nsi;
167    /// # use std::pin::Pin;
168    /// let ctx = nsi::Context::new(None).unwrap();
169    ///
170    /// // Lots of scene setup omitted ...
171    ///
172    /// // Setup a custom output driver and send
173    /// // a payload to it through the FFI boundary.
174    /// ctx.create("driver", nsi::OUTPUT_DRIVER, None);
175    /// ctx.connect("driver", None, "beauty", "outputdrivers", None);
176    ///
177    /// struct Payload {
178    ///     some_data: u32,
179    /// }
180    ///
181    /// let payload = Payload { some_data: 42 };
182    /// ctx.set_attribute(
183    ///     "driver",
184    ///     &[
185    ///         nsi::string!("drivername", "custom_driver"),
186    ///         // Payload gets sent as raw pointer through
187    ///         // the FFI boundary.
188    ///         nsi::reference!("payload", Pin::new(&payload)),
189    ///     ],
190    /// );
191    ///
192    /// // We need to explicitly call drop here as
193    /// // ctx's lifetime is pegged to that of payload.
194    /// drop(ctx);
195    /// ```
196    Reference(Reference<'b>),
197    /// A `[`[`Reference`]`]` slice.
198    References(References<'b>),
199    /// A callback.
200    Callback(Callback<'b>),
201}
202
203macro_rules! nsi_data_def {
204    ($type: ty, $name: ident, $nsi_type: expr) => {
205        /// See [`ArgData`] for details.
206        #[derive(Debug, Clone)]
207        pub struct $name {
208            data: $type,
209        }
210
211        impl $name {
212            pub fn new(data: $type) -> Self {
213                Self { data }
214            }
215        }
216
217        impl ArgDataMethods for $name {
218            fn type_(&self) -> Type {
219                $nsi_type
220            }
221
222            fn len(&self) -> usize {
223                1
224            }
225
226            fn as_c_ptr(&self) -> *const c_void {
227                &self.data as *const $type as _
228            }
229        }
230    };
231}
232
233macro_rules! nsi_data_array_def {
234    ($type: ty, $name: ident, $nsi_type: expr) => {
235        /// See [`ArgData`] for details.
236        #[derive(Debug, Clone)]
237        pub struct $name<'a> {
238            data: &'a [$type],
239        }
240
241        impl<'a> $name<'a> {
242            pub fn new(data: &'a [$type]) -> Self {
243                debug_assert_eq!(0, data.len() % $nsi_type.elemensize());
244                Self { data }
245            }
246        }
247
248        impl<'a> ArgDataMethods for $name<'a> {
249            fn type_(&self) -> Type {
250                $nsi_type
251            }
252
253            fn len(&self) -> usize {
254                self.data.len() / $nsi_type.elemensize()
255            }
256
257            fn as_c_ptr(&self) -> *const c_void {
258                self.data.as_ptr() as _
259            }
260        }
261    };
262}
263
264macro_rules! nsi_tuple_data_def {
265    ($type: tt, $len: expr, $name: ident, $nsi_type: expr) => {
266        /// See [`ArgData`] for details.
267        #[derive(Debug, Clone)]
268        pub struct $name<'a> {
269            data: &'a [$type; $len],
270        }
271
272        impl<'a> $name<'a> {
273            pub fn new(data: &'a [$type; $len]) -> Self {
274                Self { data }
275            }
276        }
277
278        impl<'a> ArgDataMethods for $name<'a> {
279            fn type_(&self) -> Type {
280                $nsi_type
281            }
282
283            fn len(&self) -> usize {
284                1
285            }
286
287            fn as_c_ptr(&self) -> *const c_void {
288                self.data.as_ptr() as _
289            }
290        }
291    };
292}
293
294nsi_data_def!(f32, Float, Type::Float);
295nsi_data_def!(f64, Double, Type::Double);
296nsi_data_def!(i32, Integer, Type::Integer);
297
298/// See [`ArgData`] for details.
299#[derive(Debug, Clone)]
300pub struct Reference<'a> {
301    data: *const c_void,
302    _marker: PhantomData<&'a ()>,
303}
304
305unsafe impl Send for Reference<'static> {}
306unsafe impl Sync for Reference<'static> {}
307
308impl<'a> Reference<'a> {
309    pub fn new<T: Sized>(data: Pin<&'a T>) -> Self {
310        Self {
311            data: data.get_ref() as *const _ as _,
312            _marker: PhantomData,
313        }
314    }
315}
316
317impl<'a> ArgDataMethods for Reference<'a> {
318    fn type_(&self) -> Type {
319        Type::Reference
320    }
321
322    fn len(&self) -> usize {
323        1
324    }
325
326    fn as_c_ptr(&self) -> *const c_void {
327        self.data
328    }
329}
330
331pub trait CallbackPtr {
332    #[doc(hidden)]
333    #[allow(clippy::wrong_self_convention)]
334    fn to_ptr(self) -> *const c_void;
335}
336
337unsafe impl Send for Callback<'static> {}
338unsafe impl Sync for Callback<'static> {}
339
340/// See [`ArgData`] for details.
341#[derive(Debug, Clone)]
342pub struct Callback<'a> {
343    data: *const c_void,
344    _marker: PhantomData<&'a mut ()>,
345}
346
347impl<'a> Callback<'a> {
348    pub fn new<T: CallbackPtr>(data: T) -> Self {
349        Self {
350            data: data.to_ptr(),
351            _marker: PhantomData,
352        }
353    }
354}
355
356impl<'a> ArgDataMethods for Callback<'a> {
357    fn type_(&self) -> Type {
358        Type::Reference
359    }
360
361    fn len(&self) -> usize {
362        1
363    }
364
365    fn as_c_ptr(&self) -> *const c_void {
366        self.data
367    }
368}
369
370/// See [`ArgData`] for details.
371#[derive(Debug, Clone)]
372pub struct String {
373    #[allow(dead_code)]
374    data: CString,
375    // The FFI API needs a pointer to a C string
376    pointer: *const c_void,
377}
378
379unsafe impl Send for String {}
380unsafe impl Sync for String {}
381
382impl String {
383    pub fn new<T: Into<Vec<u8>>>(data: T) -> Self {
384        let data = CString::new(data).unwrap();
385        let pointer = data.as_ptr() as _;
386
387        String { data, pointer }
388    }
389}
390
391impl ArgDataMethods for String {
392    fn type_(&self) -> Type {
393        Type::String
394    }
395
396    fn len(&self) -> usize {
397        1
398    }
399
400    fn as_c_ptr(&self) -> *const c_void {
401        &self.pointer as *const *const c_void as _
402    }
403}
404
405nsi_data_array_def!(f32, Floats, Type::Float);
406nsi_data_array_def!(f64, Doubles, Type::Double);
407nsi_data_array_def!(i32, Integers, Type::Integer);
408nsi_data_array_def!(f32, Colors, Type::Color);
409nsi_data_array_def!(f32, Points, Type::Point);
410nsi_data_array_def!(f32, Vectors, Type::Vector);
411nsi_data_array_def!(f32, Normals, Type::Normal);
412nsi_data_array_def!(f32, Matrices, Type::Matrix);
413nsi_data_array_def!(f64, DoubleMatrices, Type::DoubleMatrix);
414
415/// See [`ArgData`] for details.
416#[derive(Debug, Clone)]
417pub struct References<'a> {
418    data: Vec<*const c_void>,
419    _marker: PhantomData<&'a ()>,
420}
421
422unsafe impl Send for References<'static> {}
423unsafe impl Sync for References<'static> {}
424
425impl<'a> References<'a> {
426    pub fn new<T>(data: &'a [&'a T]) -> Self {
427        debug_assert_eq!(0, data.len() % Type::Reference.elemensize());
428
429        Self {
430            data: data.iter().map(|r| r as *const _ as _).collect(),
431            _marker: PhantomData,
432        }
433    }
434}
435
436impl<'a> ArgDataMethods for References<'a> {
437    fn type_(&self) -> Type {
438        Type::Reference
439    }
440
441    fn len(&self) -> usize {
442        self.data.len() / Type::Reference.elemensize()
443    }
444
445    fn as_c_ptr(&self) -> *const c_void {
446        self.data.as_ptr() as _
447    }
448}
449
450/// See [`ArgData`] for details.
451#[derive(Debug, Clone)]
452pub struct Strings {
453    #[allow(dead_code)]
454    data: Vec<CString>,
455    pointer: Vec<*const c_void>,
456}
457
458unsafe impl Send for Strings {}
459unsafe impl Sync for Strings {}
460
461impl Strings {
462    pub fn new<T: Into<Vec<u8>> + Copy>(data: &[T]) -> Self {
463        let data = data
464            .iter()
465            .map(|s| CString::new(*s).unwrap())
466            .collect::<Vec<_>>();
467        let pointer = data.iter().map(|s| s.as_ptr() as _).collect();
468
469        Strings { data, pointer }
470    }
471}
472
473impl ArgDataMethods for Strings {
474    fn type_(&self) -> Type {
475        Type::String
476    }
477
478    fn len(&self) -> usize {
479        self.pointer.len()
480    }
481
482    fn as_c_ptr(&self) -> *const c_void {
483        self.pointer.as_ptr() as _
484    }
485}
486
487nsi_tuple_data_def!(f32, 3, Color, Type::Color);
488nsi_tuple_data_def!(f32, 3, Point, Type::Point);
489nsi_tuple_data_def!(f32, 3, Vector, Type::Vector);
490nsi_tuple_data_def!(f32, 3, Normal, Type::Normal);
491nsi_tuple_data_def!(f32, 16, Matrix, Type::Matrix);
492nsi_tuple_data_def!(f64, 16, DoubleMatrix, Type::DoubleMatrix);
493
494/// Identifies an [`Arg`]’s data type.
495#[derive(Copy, Clone, Debug, PartialEq)]
496#[repr(i32)]
497pub(crate) enum Type {
498    /// A single [`f32`] value.
499    Float = NSIType::Float as _,
500    /// A single [`f64`] value.
501    Double = NSIType::Double as _,
502    /// Single [`i32`] value.
503    Integer = NSIType::Integer as _,
504    /// A [`String`].
505    String = NSIType::String as _,
506    /// Color, given as three [`f32`] values,
507    /// usually in the range `0..1`. Red would e.g. be `[1.0, 0.0,
508    /// 0.0]. Assumed to be in a linear color space.`
509    Color = NSIType::Color as _,
510    /// Point, given as three [`f32`] values.
511    Point = NSIType::Point as _,
512    /// Vector, given as three [`f32`] values.
513    Vector = NSIType::Vector as _,
514    /// Normal vector, given as three [`f32`] values.
515    Normal = NSIType::Normal as _,
516    /// Transformation matrix, given as 16 [`f32`] values.
517    Matrix = NSIType::Matrix as _,
518    /// Transformation matrix, given as 16 [`f64`] values.
519    DoubleMatrix = NSIType::DoubleMatrix as _,
520    /// Raw (`*const T`) pointer.
521    Reference = NSIType::Pointer as _,
522}
523
524impl Type {
525    /// Returns the number of components of the resp. type.
526    #[inline]
527    pub(crate) fn elemensize(&self) -> usize {
528        match self {
529            Type::Float => 1,
530            Type::Double => 1,
531            Type::Integer => 1,
532            Type::String => 1,
533            Type::Color => 3,
534            Type::Point => 3,
535            Type::Vector => 3,
536            Type::Normal => 3,
537            Type::Matrix => 16,
538            Type::DoubleMatrix => 16,
539            Type::Reference => 1,
540        }
541    }
542}
543
544/// Create a [`Float`] argument.
545#[macro_export]
546macro_rules! float {
547    ($name: tt, $value: expr) => {
548        nsi::Arg::new($name, nsi::ArgData::from(nsi::Float::new($value)))
549    };
550}
551
552/// Create a [`Floats`] array argument.
553#[macro_export]
554macro_rules! floats {
555    ($name: tt, $value: expr) => {
556        nsi::Arg::new($name, nsi::ArgData::from(nsi::Floats::new($value)))
557    };
558}
559
560/// Create a [`Double`] precision argument.
561#[macro_export]
562macro_rules! double {
563    ($name: tt, $value: expr) => {
564        nsi::Arg::new($name, nsi::ArgData::from(nsi::Double::new($value)))
565    };
566}
567
568/// Create a [`Doubles`] precision array argument.
569#[macro_export]
570macro_rules! doubles {
571    ($name: tt, $value: expr) => {
572        nsi::Arg::new($name, nsi::ArgData::from(nsi::Doubles::new($value)))
573    };
574}
575
576/// Create a [`Integer`] argument.
577#[macro_export]
578macro_rules! integer {
579    ($name: tt, $value: expr) => {
580        nsi::Arg::new($name, nsi::ArgData::from(nsi::Integer::new($value)))
581    };
582}
583
584/// Create a [`Integers`] array argument.
585#[macro_export]
586macro_rules! integers {
587    ($name: tt, $value: expr) => {
588        nsi::Arg::new($name, nsi::ArgData::from(nsi::Integers::new($value)))
589    };
590}
591
592/// Create a [`Color`] argument.
593#[macro_export]
594macro_rules! color {
595    ($name: tt, $value: expr) => {
596        nsi::Arg::new($name, nsi::ArgData::from(nsi::Color::new($value)))
597    };
598}
599
600/// Create a [`Colors`] array argument.
601#[macro_export]
602macro_rules! colors {
603    ($name: tt, $value: expr) => {
604        nsi::Arg::new($name, nsi::ArgData::from(nsi::Colors::new($value)))
605    };
606}
607
608/// Create a [`Point`] argument.
609#[macro_export]
610macro_rules! point {
611    ($name: tt, $value: expr) => {
612        nsi::Arg::new($name, nsi::ArgData::from(nsi::Point::new($value)))
613    };
614}
615
616/// Create a [`Points`] array argument.
617#[macro_export]
618macro_rules! points {
619    ($name: tt, $value: expr) => {
620        nsi::Arg::new($name, nsi::ArgData::from(nsi::Points::new($value)))
621    };
622}
623
624/// Create a [`Vector`] argument.
625#[macro_export]
626macro_rules! vector {
627    ($name: tt, $value: expr) => {
628        nsi::Arg::new($name, nsi::ArgData::from(nsi::Vector::new($value)))
629    };
630}
631
632/// Create a [`Vectors`] array argument.
633#[macro_export]
634macro_rules! vectors {
635    ($name: tt, $value: expr) => {
636        nsi::Arg::new($name, nsi::ArgData::from(nsi::Vectors::new($value)))
637    };
638}
639
640/// Create a [`Normal`] argument.
641#[macro_export]
642macro_rules! normal {
643    ($name: tt, $value: expr) => {
644        nsi::Arg::new($name, nsi::ArgData::from(nsi::Normal::new($value)))
645    };
646}
647
648/// Create a [`Normals`] array argument.
649#[macro_export]
650macro_rules! normals {
651    ($name: tt, $value: expr) => {
652        nsi::Arg::new($name, nsi::ArgData::from(nsi::Normals::new($value)))
653    };
654}
655
656/// Create a [`Matrix`] row-major, 4×4 transformation matrix argument.
657/// The matrix is given as 16 [`f32`] values.
658#[macro_export]
659macro_rules! matrix {
660    ($name: tt, $value: expr) => {
661        nsi::Arg::new($name, nsi::ArgData::from(nsi::Matrix::new($value)))
662    };
663}
664
665/// Create a [`Matrices`] row-major, 4×4 transformation matrices argument.
666/// Each matrix is given as 16 [`f32`] values.
667#[macro_export]
668macro_rules! matrices {
669    ($name: tt, $value: expr) => {
670        nsi::Arg::new($name, nsi::ArgData::from(nsi::Matrices::new($value)))
671    };
672}
673
674/// Create a [`DoubleMatrix`] row-major, 4×4 transformation matrix argument.
675/// The matrix is given as 16 [`f64`] values.
676///
677/// # Examples
678///
679/// ```
680/// # use nsi_core as nsi;
681/// # let ctx = nsi::Context::new(None).unwrap();
682/// // Setup a transform node.
683/// ctx.create("xform", nsi::TRANSFORM, None);
684/// ctx.connect("xform", None, nsi::ROOT, "objects", None);
685///
686/// // Translate 5 units along z-axis.
687/// ctx.set_attribute(
688///     "xform",
689///     &[nsi::double_matrix!(
690///         "transformationmatrix",
691///         &[1., 0., 0., 0., 0., 1., 0., 0., 0., 0., 1., 0., 0., 0., 5., 1.,]
692///     )],
693/// );
694/// ```
695#[macro_export]
696macro_rules! double_matrix {
697    ($name: tt, $value: expr) => {
698        nsi::Arg::new($name, nsi::ArgData::from(nsi::DoubleMatrix::new($value)))
699    };
700}
701
702/// Create a [`DoubleMatrices`] row-major, 4×4 transformation matrices argument.
703/// Each matrix is given as 16 [`f64`] values.
704#[macro_export]
705macro_rules! double_matrices {
706    ($name: tt, $value: expr) => {
707        nsi::Arg::new(
708            $name,
709            nsi::ArgData::from(nsi::DoubleMatrices::new($value)),
710        )
711    };
712}
713
714/// Create a [`String`] argument.
715///
716/// # Examples
717///
718/// ```
719/// # use nsi_core as nsi;
720/// // Create rendering context.
721/// let ctx =
722///     nsi::Context::new(Some(&[nsi::string!("streamfilename", "stdout")]))
723///         .expect("Could not create NSI context.");
724/// ```
725#[macro_export]
726macro_rules! string {
727    ($name: tt, $value: expr) => {
728        nsi::Arg::new($name, nsi::ArgData::from(nsi::String::new($value)))
729    };
730}
731
732/// Create a [`String`] array argument.
733///
734/// # Examples
735///
736/// ```
737/// # use nsi_core as nsi;
738/// # let ctx = nsi::Context::new(None).unwrap();
739/// // One of these is not an actor:
740/// ctx.set_attribute(
741///     "dummy",
742///     &[nsi::strings!(
743///         "actors",
744///         &["Klaus Kinski", "Giorgio Moroder", "Rainer Brandt"]
745///     )],
746/// );
747/// ```
748#[macro_export]
749macro_rules! strings {
750    ($name: tt, $value: expr) => {
751        nsi::Arg::new($name, nsi::ArgData::from(nsi::Strings::new($value)))
752    };
753}
754
755/// Create a [`Reference`] argument.
756#[macro_export]
757macro_rules! reference {
758    ($name: tt, $value: expr) => {
759        nsi::Arg::new($name, nsi::ArgData::from(nsi::Reference::new($value)))
760    };
761}
762
763/// Create a [`Reference`] array argument.
764#[macro_export]
765macro_rules! references {
766    ($name: tt, $value: expr) => {
767        nsi::Arg::new($name, nsi::ArgData::from(nsi::References::new($value)))
768    };
769}
770
771/// Create a [`Callback`] argument.
772#[macro_export]
773macro_rules! callback {
774    ($name: tt, $value: expr) => {
775        nsi::Arg::new($name, nsi::ArgData::from(nsi::Callback::new($value)))
776    };
777}