Skip to content

Commit ebf4427

Browse files
bors[bot]B-headBromeon
authored
Merge #886
886: Add TryFrom to convert from Varargs to tuples r=Bromeon a=B-head This PR is stacked on `varargs_gets` branch. See #892 for details on some of the commits included in this PR. ## Feature Dirty syntax to convert to tuples. This makes manual binding easier to write than ever before. Implement via `varargs_into_tuple!()` macro. Can be converted to tuples up to 12 in length. This is the same length supported by standard library. ```rust fn call(&self, _this: TInstance<'_, Foo>, args: Varargs<'_>) -> Variant { // Convert from Varargs to tuples. let (a, b, c): (i64, i64, i64) = args.try_into().unwrap(); let ret = (a * b - c); ret.to_variant() } ``` ## Compatibility Since this PR only adds an API, there are no compatibility issues. Co-authored-by: B_head <[email protected]> Co-authored-by: Jan Haller <[email protected]>
2 parents bacc380 + 461b72e commit ebf4427

File tree

2 files changed

+402
-12
lines changed

2 files changed

+402
-12
lines changed

gdnative-core/src/export/method.rs

+310-12
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
//! Method registration
22
33
use std::borrow::Cow;
4-
use std::fmt;
54
use std::marker::PhantomData;
5+
use std::{fmt, ops};
66

77
use crate::core_types::{FromVariant, FromVariantError, Variant};
88
use crate::export::class::NativeClass;
@@ -209,17 +209,45 @@ impl<C: NativeClass, F: StaticArgsMethod<C>> Method<C> for StaticArgs<F> {
209209
}
210210

211211
/// Safe interface to a list of borrowed method arguments with a convenient API
212-
/// for common operations with them. Can also be used as an iterator.
212+
/// for common operations with them.
213+
///
214+
/// **Note:** the `impl Iterator` is deprecated, `Varargs` itself should not be treated as a consumable iterator.
215+
/// Instead, use [`Self::as_slice()`].
216+
///
217+
/// This type can be destructured into tuples:
218+
/// ```no_run
219+
/// use gdnative::prelude::*;
220+
/// use gdnative::export::Varargs;
221+
///
222+
/// #[derive(NativeClass)]
223+
/// #[no_constructor]
224+
/// struct MyClass {}
225+
///
226+
/// struct CalcMethod;
227+
/// impl Method<MyClass> for CalcMethod {
228+
/// fn call(
229+
/// &self,
230+
/// _this: TInstance<'_, MyClass>,
231+
/// args: Varargs<'_>,
232+
/// ) -> Variant {
233+
/// // Destructure via try_into():
234+
/// let (a, b): (i64, i64) = args.try_into().expect("signature mismatch");
235+
/// let ret = a * b;
236+
/// ret.to_variant()
237+
/// }
238+
/// }
239+
/// ```
213240
pub struct Varargs<'a> {
214241
idx: usize,
215-
iter: std::slice::Iter<'a, &'a Variant>,
242+
args: &'a [&'a Variant],
243+
offset_index: usize,
216244
}
217245

218246
impl<'a> Varargs<'a> {
219247
/// Returns the amount of arguments left.
220248
#[inline]
221249
pub fn len(&self) -> usize {
222-
self.iter.len()
250+
self.args.len() - self.idx
223251
}
224252

225253
#[inline]
@@ -250,7 +278,7 @@ impl<'a> Varargs<'a> {
250278
/// Returns the remaining arguments as a slice of `Variant`s.
251279
#[inline]
252280
pub fn as_slice(&self) -> &'a [&'a Variant] {
253-
self.iter.as_slice()
281+
self.args
254282
}
255283

256284
/// Discard the rest of the arguments, and return an error if there is any.
@@ -284,7 +312,87 @@ impl<'a> Varargs<'a> {
284312
let args = std::mem::transmute::<&[*mut sys::godot_variant], &[&Variant]>(args);
285313
Self {
286314
idx: 0,
287-
iter: args.iter(),
315+
args,
316+
offset_index: 0,
317+
}
318+
}
319+
320+
/// Check the length of arguments.
321+
///
322+
/// See [`Self::get()`] or [`Self::get_opt()`] for examples.
323+
///
324+
/// # Errors
325+
/// Returns an [`VarargsError::InvalidLength`] if the length of arguments is outside the specified range.
326+
#[inline]
327+
pub fn check_length(&self, expected: impl Into<IndexBounds>) -> Result<(), VarargsError> {
328+
let passed = self.args.len();
329+
let expected = expected.into();
330+
if expected.contains(passed) {
331+
Ok(())
332+
} else {
333+
// Note: cannot use Box<dyn RangeBounds<usize>> because trait is not object-safe due to _unrelated_ method contains()
334+
Err(VarargsError::InvalidLength {
335+
length: passed,
336+
expected,
337+
})
338+
}
339+
}
340+
341+
/// Returns the type-converted value at the specified argument position.
342+
///
343+
/// # Errors
344+
/// Returns a [`VarargsError::InvalidArgumentType`] if the conversion fails or the argument is not set.
345+
///
346+
/// # Examples
347+
/// ```
348+
/// # fn call(args: gdnative::export::Varargs) -> Result<(), Box<dyn std::error::Error>> {
349+
/// args.check_length(2)?;
350+
/// let a: usize = args.get(0)?;
351+
/// let rest: i64 = args.get(1)?;
352+
/// # Ok(())
353+
/// # }
354+
/// ```
355+
#[inline]
356+
pub fn get<T: FromVariant>(&self, index: usize) -> Result<T, VarargsError> {
357+
// Note: disregards iterator offset, since that representation is deprecated
358+
359+
match self.args.get(index) {
360+
Some(v) => match T::from_variant(v) {
361+
Ok(ok) => Ok(ok),
362+
Err(error) => Err(VarargsError::InvalidArgumentType { index, error }),
363+
},
364+
None => {
365+
let error = FromVariantError::Custom("Argument is not set".to_owned());
366+
Err(VarargsError::InvalidArgumentType { index, error })
367+
}
368+
}
369+
}
370+
371+
/// Returns the type-converted value at the specified argument position.
372+
/// Returns `None` if the argument is not set.
373+
///
374+
/// # Errors
375+
/// Returns a [`VarargsError::InvalidArgumentType`] if the conversion fails.
376+
///
377+
/// # Examples
378+
/// ```
379+
/// # fn call(args: gdnative::export::Varargs) -> Result<(), Box<dyn std::error::Error>> {
380+
/// args.check_length(1..=2)?;
381+
/// let a: usize = args.get(0)?;
382+
/// let rest: i64 = args.get_opt(1)?.unwrap_or(72);
383+
/// # Ok(())
384+
/// # }
385+
/// ```
386+
#[inline]
387+
pub fn get_opt<T: FromVariant>(&self, index: usize) -> Result<Option<T>, VarargsError> {
388+
// Note: disregards iterator offset, since that representation is deprecated
389+
390+
match self.args.get(index) {
391+
Some(v) => match T::from_variant(v) {
392+
Ok(ok) => Ok(Some(ok)),
393+
Err(error) => Err(VarargsError::InvalidArgumentType { index, error }),
394+
},
395+
None => Ok(None),
288396
}
289397
}
290398
}
@@ -293,7 +401,197 @@ impl<'a> Iterator for Varargs<'a> {
293401
type Item = &'a Variant;
294402
#[inline]
295403
fn next(&mut self) -> Option<Self::Item> {
296-
self.iter.next().copied()
404+
let ret = self.args.get(self.idx);
405+
ret.map(|&v| {
406+
self.idx += 1;
407+
v
408+
})
409+
}
410+
}
411+
412+
// Return a second token.
413+
macro_rules! replace_expr {
414+
($_t:tt $sub:expr) => {
415+
$sub
416+
};
417+
}
418+
419+
// Count parameters.
420+
macro_rules! count_tts {
421+
($($tts:tt)*) => {
422+
0usize $(+ replace_expr!($tts 1usize))*
423+
};
424+
}
425+
426+
// Convert from Varargs to tuples, implement traits.
427+
macro_rules! varargs_into_tuple {
428+
($($params:ident),*) => {
429+
impl<'a, $($params: FromVariant),*> std::convert::TryFrom<Varargs<'a>> for ($($params,)*) {
430+
type Error = VarargsError;
431+
432+
#[inline]
433+
fn try_from(args: Varargs<'a>) -> Result<Self, Self::Error> {
434+
const EXPECTED: usize = count_tts!($($params)*);
435+
args.check_length(EXPECTED)?;
436+
let mut i: usize = 0;
437+
#[allow(unused_variables, unused_mut)]
438+
let mut inc = || {
439+
let ret = i;
440+
i += 1;
441+
ret
442+
};
443+
Ok((
444+
$(args.get::<$params>(inc())?,)*
445+
))
446+
}
447+
}
448+
};
449+
}
450+
451+
// Define up to the length supported by standard library.
452+
varargs_into_tuple!();
453+
varargs_into_tuple!(A);
454+
varargs_into_tuple!(A, B);
455+
varargs_into_tuple!(A, B, C);
456+
varargs_into_tuple!(A, B, C, D);
457+
varargs_into_tuple!(A, B, C, D, E);
458+
varargs_into_tuple!(A, B, C, D, E, F);
459+
varargs_into_tuple!(A, B, C, D, E, F, G);
460+
varargs_into_tuple!(A, B, C, D, E, F, G, H);
461+
varargs_into_tuple!(A, B, C, D, E, F, G, H, I);
462+
varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J);
463+
varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J, K);
464+
varargs_into_tuple!(A, B, C, D, E, F, G, H, I, J, K, L);
465+
466+
/// All possible errors that can occur when converting from Varargs.
467+
///
468+
/// For methods that return this error, see [`Varargs::check_length()`], [`Varargs::get()`] or [`Varargs::get_opt()`].
469+
/// Another context where this type is used is when destructuring `Varargs` into tuples.
470+
#[derive(Debug)]
471+
pub enum VarargsError {
472+
/// At least one argument type mismatches.
473+
InvalidArgumentType {
474+
index: usize,
475+
error: FromVariantError,
476+
},
477+
/// Number of arguments doesn't match expectations.
478+
InvalidLength {
479+
/// The number of arguments actually passed.
480+
length: usize,
481+
expected: IndexBounds,
482+
},
483+
}
484+
485+
impl std::error::Error for VarargsError {}
486+
487+
impl fmt::Display for VarargsError {
488+
#[inline]
489+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
490+
match self {
491+
VarargsError::InvalidArgumentType { index, error } => {
492+
write!(f, "type error for argument #{}: {}", index, error)?
493+
}
494+
VarargsError::InvalidLength { expected, length } => write!(
495+
f,
496+
"length mismatch: expected range {}, actual {}",
497+
expected, length
498+
)?,
499+
}
500+
501+
Ok(())
502+
}
503+
}
504+
505+
/// Defines which number of arguments is valid.
506+
///
507+
/// Convertible from an exact value `a` as well as inclusive range expressions `a..`, `..=b`, `a..=b`.
508+
pub struct IndexBounds {
509+
/// The lower (inclusive) bound of the expected number of arguments, or `None` if unbounded.
510+
pub start: Option<usize>,
511+
512+
/// The upper (inclusive) bound of the expected number of arguments, or `None` if unbounded.
513+
pub end: Option<usize>,
514+
}
515+
516+
impl IndexBounds {
517+
#[inline]
518+
pub fn contains(&self, value: usize) -> bool {
519+
match (self.start, self.end) {
520+
(Some(s), Some(e)) => value >= s && value <= e,
521+
(Some(s), None) => value >= s,
522+
(None, Some(e)) => value <= e,
523+
(None, None) => false, // unreachable in this context, but can be constructed by user
524+
}
525+
}
526+
}
527+
528+
/// `a`
529+
impl From<usize> for IndexBounds {
530+
#[inline]
531+
fn from(exact_value: usize) -> Self {
532+
Self {
533+
start: Some(exact_value),
534+
end: Some(exact_value),
535+
}
536+
}
537+
}
538+
539+
/// `a..=b`
540+
impl From<ops::RangeInclusive<usize>> for IndexBounds {
541+
#[inline]
542+
fn from(range: ops::RangeInclusive<usize>) -> Self {
543+
Self {
544+
start: Some(*range.start()),
545+
end: Some(*range.end()),
546+
}
547+
}
548+
}
549+
550+
/// `a..`
551+
impl From<ops::RangeFrom<usize>> for IndexBounds {
552+
#[inline]
553+
fn from(range: ops::RangeFrom<usize>) -> Self {
554+
Self {
555+
start: Some(range.start),
556+
end: None,
557+
}
558+
}
559+
}
560+
561+
/// `..=b`
562+
impl From<ops::RangeToInclusive<usize>> for IndexBounds {
563+
#[inline]
564+
fn from(range: ops::RangeToInclusive<usize>) -> Self {
565+
Self {
566+
start: None,
567+
end: Some(range.end),
568+
}
569+
}
570+
}
571+
572+
impl fmt::Debug for IndexBounds {
573+
#[inline]
574+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
575+
write!(f, "IndexBounds({})", self)
576+
}
577+
}
578+
579+
impl fmt::Display for IndexBounds {
580+
#[inline]
581+
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
582+
match self.start {
583+
Some(start) => write!(f, "{}", start)?,
584+
None => {}
585+
}
586+
587+
write!(f, "..=")?;
588+
589+
match self.end {
590+
Some(end) => write!(f, "{}", end)?,
591+
None => {}
592+
}
593+
594+
Ok(())
297595
}
298596
}
299597

@@ -361,10 +659,11 @@ impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
361659
#[inline]
362660
pub fn get(mut self) -> Result<T, ArgumentError<'a>> {
363661
self.get_optional_internal().and_then(|arg| {
662+
let actual_index = self.args.idx + self.args.offset_index;
364663
arg.ok_or(ArgumentError {
365664
site: self.site,
366665
kind: ArgumentErrorKind::Missing {
367-
idx: self.args.idx,
666+
idx: actual_index,
368667
name: self.name,
369668
},
370669
})
@@ -389,14 +688,13 @@ impl<'r, 'a, T: FromVariant> ArgBuilder<'r, 'a, T> {
389688
ty,
390689
..
391690
} = self;
392-
let idx = args.idx;
691+
let actual_index = args.idx + args.offset_index;
393692

394-
if let Some(arg) = args.iter.next() {
395-
args.idx += 1;
693+
if let Some(arg) = args.next() {
396694
T::from_variant(arg).map(Some).map_err(|err| ArgumentError {
397695
site: *site,
398696
kind: ArgumentErrorKind::CannotConvert {
399-
idx,
697+
idx: actual_index,
400698
name: name.take(),
401699
value: arg,
402700
ty: ty

0 commit comments

Comments
 (0)