1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
use sys::{self, VALUE};

use super::{CheckResult, FromRuby, ToRuby, ToRubyResult};
use super::super::{inspect};

#[doc(hidden)]
macro_rules! impl_tuple_coercions {
    ($($name:ident),*) => {
        impl_tuple_from_ruby!(count_items!($($name),*), $($name),*);
        impl_tuple_to_ruby!(count_items!($($name),*), $($name),*);
    };

    ($($any:tt)*) => {
      compile_error!(stringify!("impl_tuple_coercions" $($any)*));
    };
}

#[doc(hidden)]
macro_rules! impl_tuple_from_ruby {
    ($count:expr, $($name:ident),*) => {
        impl<$($name: FromRuby,)*> FromRuby for ($($name,)*) {
            type Checked = ($($name::Checked,)*);

            fn from_ruby(value: VALUE) -> CheckResult<Self::Checked> {
                if unsafe { sys::RB_TYPE_P(value, sys::T_ARRAY) } {
                    // Make sure we can actually do the conversions for the values.
                    let len = unsafe { sys::RARRAY_LEN(value) };

                    if len != $count {
                        type_error!(value, format!("an array with {} {}", $count, { if $count == 1 { "element" } else { "elements" } }))
                    }

                    extract_tuple_elements_from_ruby!(value, (0), $($name),*);

                    Ok(($($name,)*))
                } else {
                    type_error!(value, "an array")
                }
            }

            fn from_checked(checked: Self::Checked) -> Self {
                #[allow(non_snake_case)]
                let ($($name,)*) = checked;
                ($($name::from_checked($name),)*)
            }
        }
    };

    ($($any:tt)*) => {
      compile_error!(stringify!("impl_tuple_from_ruby" $($any)*));
    };
}

#[doc(hidden)]
macro_rules! extract_tuple_elements_from_ruby {
    ($value:ident, $offset:tt) => {};
    ($value:ident, $offset:tt, $name:ident $($rest:tt)*) => {
        #[allow(non_snake_case)]
        let $name = {
            let val = unsafe { sys::rb_ary_entry($value, $offset as isize) };
            match $name::from_ruby(val) {
                Ok(v) => v,
                Err(e) => type_error!(format!("Failed to convert {}, element {} has the wrong type: {}", inspect($value), $offset, e)),
            }
        };

        extract_tuple_elements_from_ruby!($value, ($offset + 1) $($rest)*);
    };

    ($($any:tt)*) => {
      compile_error!(stringify!("extract_tuple_elements_from_ruby" $($any)*));
    };
}

#[doc(hidden)]
macro_rules! impl_tuple_to_ruby {
    ($count:expr, $($name:ident),*) => {
        impl<$($name: ToRuby,)*> ToRuby for ($($name,)*) {
            fn to_ruby(self) -> ToRubyResult {
                let ary = unsafe { sys::rb_ary_new_capa($count as isize) };

                #[allow(non_snake_case)]
                let ($($name,)*) = self;


                $(
                    unsafe { sys::rb_ary_push(ary, $name.to_ruby()?); }
                )*;

                Ok(ary)
            }
        }
    };

    ($($any:tt)*) => {
        compile_error!(stringify!("impl_tuple_to_ruby" $($any)*));
    };
}

#[doc(hidden)]
macro_rules! count_items {
    () => { 0 };
    ($item:tt $(, $rest:tt)*) => { 1 + count_items!($($rest),*) };

  ($($any:tt)*) => {
    compile_error!(stringify!("count_items" $($any)*));
  };
}

impl_tuple_coercions!(A);
impl_tuple_coercions!(A, B);
impl_tuple_coercions!(A, B, C);
impl_tuple_coercions!(A, B, C, D);
impl_tuple_coercions!(A, B, C, D, E);
impl_tuple_coercions!(A, B, C, D, E, F);
impl_tuple_coercions!(A, B, C, D, E, F, G);
impl_tuple_coercions!(A, B, C, D, E, F, G, H);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I, J);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I, J, K);
impl_tuple_coercions!(A, B, C, D, E, F, G, H, I, J, K, L);