1use crate::from_r::{SexpError, SexpLengthError, SexpTypeError, TryFromSexp};
15use crate::{SEXP, SEXPTYPE, SexpExt};
16
17fn check_list_shape(sexp: SEXP, expected_len: usize, len: usize) -> Result<(), SexpError> {
18 let actual = sexp.type_of();
19 if actual != SEXPTYPE::VECSXP {
20 return Err(SexpTypeError {
21 expected: SEXPTYPE::VECSXP,
22 actual,
23 }
24 .into());
25 }
26 if len != expected_len {
27 return Err(SexpLengthError {
28 expected: expected_len,
29 actual: len,
30 }
31 .into());
32 }
33 Ok(())
34}
35
36fn batch_tuple_errors(errors: Vec<String>) -> SexpError {
37 SexpError::InvalidValue(format!("tuple conversion failed: {}", errors.join("; ")))
38}
39
40macro_rules! impl_tuple_try_from_sexp {
43 (($($T:ident),+), ($($idx:tt),+), $n:expr) => {
44 impl<$($T: TryFromSexp),+> TryFromSexp for ($($T,)+)
45 where
46 $(<$T as TryFromSexp>::Error: Into<SexpError>,)+
47 {
48 type Error = SexpError;
49
50 fn try_from_sexp(sexp: SEXP) -> Result<Self, Self::Error> {
51 check_list_shape(sexp, $n, sexp.len())?;
52
53 let mut errors: Vec<String> = Vec::new();
54 let partial = (
55 $(
56 match <$T as TryFromSexp>::try_from_sexp(
57 sexp.vector_elt($idx as isize),
58 ) {
59 Ok(v) => Some(v),
60 Err(e) => {
61 errors.push(format!(
62 "element {}: {}",
63 $idx + 1,
64 Into::<SexpError>::into(e)
65 ));
66 None
67 }
68 },
69 )+
70 );
71 if !errors.is_empty() {
72 return Err(batch_tuple_errors(errors));
73 }
74 Ok(($(partial.$idx.unwrap(),)+))
76 }
77
78 unsafe fn try_from_sexp_unchecked(sexp: SEXP) -> Result<Self, Self::Error> {
79 check_list_shape(sexp, $n, unsafe { sexp.len_unchecked() })?;
80
81 let mut errors: Vec<String> = Vec::new();
82 let partial = (
83 $(
84 match unsafe {
85 <$T as TryFromSexp>::try_from_sexp_unchecked(
86 sexp.vector_elt_unchecked($idx as isize),
87 )
88 } {
89 Ok(v) => Some(v),
90 Err(e) => {
91 errors.push(format!(
92 "element {}: {}",
93 $idx + 1,
94 Into::<SexpError>::into(e)
95 ));
96 None
97 }
98 },
99 )+
100 );
101 if !errors.is_empty() {
102 return Err(batch_tuple_errors(errors));
103 }
104 Ok(($(partial.$idx.unwrap(),)+))
105 }
106 }
107 };
108}
109
110impl_tuple_try_from_sexp!((A), (0), 1);
112impl_tuple_try_from_sexp!((A, B), (0, 1), 2);
113impl_tuple_try_from_sexp!((A, B, C), (0, 1, 2), 3);
114impl_tuple_try_from_sexp!((A, B, C, D), (0, 1, 2, 3), 4);
115impl_tuple_try_from_sexp!((A, B, C, D, E), (0, 1, 2, 3, 4), 5);
116impl_tuple_try_from_sexp!((A, B, C, D, E, F), (0, 1, 2, 3, 4, 5), 6);
117impl_tuple_try_from_sexp!((A, B, C, D, E, F, G), (0, 1, 2, 3, 4, 5, 6), 7);
118impl_tuple_try_from_sexp!((A, B, C, D, E, F, G, H), (0, 1, 2, 3, 4, 5, 6, 7), 8);