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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
//! Impl for tuples
use crate::DeserializeErrors;
use nonempty::NonEmpty;
use pastey::paste;
use seq_macro::seq;
use crate::{Key, Value};
use std::borrow::Cow;
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
use crate::util::value_type_description;
use crate::{DeserializeValue, Error};
/// Generate impl for each tuple
macro_rules! impl_for_tuples {
( $( $arity:literal )* ) => {
$(
seq!(I in 0..$arity {
paste! {
#[doc = concat!("Failed to deserialize tuple with ", stringify!($arity), " items")]
#[derive(Diagnostic, Error, Debug, Clone, Eq, PartialEq, Hash)]
#[error("Failed to deserialize a tuple of size {}", stringify!($arity))]
#[allow(clippy::large_enum_variant, reason = "hard to fix it in a macro like this")]
pub enum [<DeserializeTuple $arity Error>]<
#( [<E I>], )*
>
where
#( [<E I>]: Error, )*
{
/// The TOML value is not an array
#[error("Type mismatch")]
NotArray {
/// Details on why deserialization failed
details: String,
/// Location of the error in the source file
#[label("{details}")]
span: SourceSpan,
},
#[doc = concat!("The TOML array does not have exactly ", $arity, " elements")]
#[error("Tuple length mismatch: expected {expected_length}, found {actual_length}")]
#[diagnostic(help(
"The TOML array must contain exactly {expected_length} elements for this tuple."
))]
LengthMismatch {
/// The length that was expected
expected_length: usize,
/// The length that we got
actual_length: usize,
/// Location of the error in the source file
#[label("this tuple has {actual_length} elements")]
span: SourceSpan,
#(
#[doc = concat!("Errors from the deserialization of the tuple element ", I)]
#[related]
[< element I _errors >]: Vec< [< E I >] >,
)*
},
#(
#[doc = concat!("Error from deserializing element ", I, " of the tuple")]
#[error(transparent)]
#[diagnostic(transparent)]
[< Element I Error >]([< E I >]),
)*
}
// Implement TypeMismatchError for DeserializeTupleError.
// This implementation assumes E1 is the primary error type for the `new` and `expected_type` methods.
impl< #( [< E I >]: Error, )*> Error
for [< DeserializeTuple $arity Error>]< #( [< E I >], )* >
{
/// Returns a string describing the expected type for the tuple.
///
/// For a tuple, the expected type is simply "a tuple".
///
/// More specific details about inner types are handled by the element-specific errors
fn expected_type() -> Cow<'static, str> {
let list: Vec<std::borrow::Cow<'static, str>> = vec![
#( [< E I >]::expected_type(), )*
];
let list = list.join(", ");
format!("list: [{list}]").into()
}
}
/// Implements `DeserializeValue` for tuples `(T, K)`.
///
/// This allows deserializing a TOML array of exactly two elements into a Rust tuple.
impl<'v, #( [< T I >], )* > DeserializeValue<'v> for ( #( [< T I >], )* )
where
#(
[< T I >]: DeserializeValue<'v>,
)*
{
type Error = [< DeserializeTuple $arity Error>]< #( [< T I >]::Error, )* >;
fn deserialize_value<'k>(
_key: Key<'k>,
value: Value<'v>
) -> Result<Self, DeserializeErrors<Self, NonEmpty<Self::Error>>> {
let span = value.span();
let description = value_type_description(&value);
let toml::de::DeValue::Array(xs) = value.into_inner() else {
// Not an array at all
return Err(DeserializeErrors::err(
[< DeserializeTuple $arity Error>]::NotArray {
span: span.into(),
details: description.to_string()
}
));
};
if xs.len() != $arity {
// Length mismatch
return Err(DeserializeErrors::err(
[< DeserializeTuple $arity Error>]::LengthMismatch {
expected_length: $arity,
actual_length: xs.len(),
span: span.into(),
// No inner errors from element deserialization yet, as the length was wrong
#(
[< element I _errors >]: vec![],
)*
}
));
}
#[allow(unused_mut, reason = "macro")]
let mut tuple_errors = Vec::new();
#[allow(unused_mut, unused_variables, reason = "macro")]
let mut xs = xs.into_iter();
#(
let [< val_ e I >] = match [< T I >]::deserialize_value(
Key::Index(I),
xs.next().expect(concat!("len == ", stringify!($arity)))
) {
Ok(ok) => {
Some(ok)
}
Err(DeserializeErrors { recovered, errors }) => {
for e in errors {
tuple_errors.push([< DeserializeTuple $arity Error>]::[< Element I Error >](e));
}
recovered
}
};
)*
#[allow(irrefutable_let_patterns, reason = "macro")]
let ( #(Some( [< _e I >] ), )* ) = ( #( [< val_ e I >] ,)* ) else {
// One or both elements failed to deserialize.
// `tuple_errors` already contains the specific element errors.
return Err(DeserializeErrors {
recovered: None,
errors: NonEmpty::from_vec(tuple_errors).expect(
concat!(
"if at least 1 `<T I>::from_value` failed, it would have created ",
"at least 1 error because `errs` is `NonEmpty` in all branches"
)
)
});
};
if let Some(tuple_errors) = NonEmpty::from_vec(tuple_errors) {
Err(DeserializeErrors {
recovered: Some(( #( [< _e I >], )* )),
errors: tuple_errors,
})
} else {
Ok(( #( [< _e I >], )* ))
}
}
}
}
});
)*
};
}
seq!(N in 0..=16 {
impl_for_tuples!(#(N)*);
});