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
//! Deserialize into `[T; N]`
use crate::util::deserialize_sequence;
use crate::{DeserializeErrors, util::value_type_description};
use nonempty::NonEmpty;
use std::borrow::Cow;
use miette::{Diagnostic, SourceSpan};
use thiserror::Error;
use crate::{DeserializeValue, Error, Key, Value};
use super::sequence::DeserializeSequenceError;
// TODO: Figure out how to have a single variant for `LengthMismatch` and `LengthMismatchAndCollectionError`
/// Failed to deserialize `[T; N]`
#[derive(Diagnostic, Error, Debug, Clone, Eq, PartialEq, Hash)]
pub enum DeserializeArrayError<E: Error, const N: usize> {
/// Wraps a general `DeserializeCollectionError` for cases where the TOML value
/// is not an array or has unrecoverable element-level errors.
#[error(transparent)]
#[diagnostic(transparent)]
CollectionError(DeserializeSequenceError<E>),
/// Represents an error where the deserialized array has a different number of elements
/// than expected for the fixed-size array `[T; N]`.
#[error("Array length mismatch")]
#[diagnostic(help("this array must contain exactly {N} elements."))]
LengthMismatch {
/// How many elements we got
actual_length: usize,
/// Location of the error in the source file
#[label("this array has {actual_length} elements")]
span: SourceSpan,
/// Inner errors from individual element deserialization, if any occurred
/// before the length mismatch was detected.
#[related]
inner_errors: Vec<E>,
},
/// Represents an error where the deserialized array has a different number of elements
/// than expected for the fixed-size array `[T; N]`.
#[error("Array length mismatch")]
#[diagnostic(help("this array must contain exactly {N} elements."))]
LengthMismatchAndCollectionError {
/// How many elements we got
actual_length: usize,
/// Location of the error in the source file
#[label("this array has {actual_length} elements")]
span: SourceSpan,
/// Inner errors from individual element deserialization, if any occurred
/// before the length mismatch was detected.
#[diagnostic(transparent)]
inner_errors: DeserializeSequenceError<E>,
},
}
impl<E: Error, const N: usize> Error for DeserializeArrayError<E, N> {
fn expected_type() -> Cow<'static, str> {
format!("array of {N} {}", E::expected_type()).into()
}
}
impl<'de, const N: usize, T: DeserializeValue<'de>> DeserializeValue<'de> for [T; N] {
type Error = DeserializeArrayError<T::Error, N>;
fn deserialize_value<'k>(
_key: Key<'k>,
value: Value<'de>,
) -> Result<Self, DeserializeErrors<Self, NonEmpty<Self::Error>>> {
let span = value.span();
match deserialize_sequence(value, T::deserialize_value, |value| {
DeserializeArrayError::<T::Error, N>::details(value_type_description(value))
}) {
Ok(seq) => {
let len = seq.len();
seq.try_into().map_err(|_| {
// TODO: unfortunately, we have to discard all of the values that
// we obtained simply because of the length mismatch :/
DeserializeErrors::err(DeserializeArrayError::LengthMismatch {
actual_length: len,
span: span.into(),
inner_errors: vec![],
})
})
}
Err((Some(recovered_seq), err)) => {
let len = recovered_seq.len();
let array: Result<[T; N], _> = recovered_seq.try_into();
Err(match array {
// The array has recoverable errors, and it's length is correct
Ok(recovered) => DeserializeErrors::recovered(
recovered,
DeserializeArrayError::CollectionError(err),
),
// The array has recoverable errors, but it's length is incorrect
Err(_) => DeserializeErrors::err(
DeserializeArrayError::LengthMismatchAndCollectionError {
actual_length: len,
span: span.into(),
inner_errors: err,
},
),
})
}
Err((None, err)) => Err(DeserializeErrors::err(
DeserializeArrayError::CollectionError(err),
)),
}
}
}