1use std::fmt::{self, Display, Formatter};
2
3pub(crate) const MAX_ERROR_MESSAGE_BYTES: usize = 4096;
4
5#[derive(Debug)]
7pub struct MapError {
8 index: usize,
9 message: String,
10}
11
12impl MapError {
13 pub fn index(&self) -> usize {
15 self.index
16 }
17
18 pub fn message(&self) -> &str {
20 &self.message
21 }
22
23 fn new<E: Display>(index: usize, error: E) -> Self {
24 Self {
25 index,
26 message: truncate_message(error.to_string()),
27 }
28 }
29}
30
31impl Display for MapError {
32 fn fmt(&self, formatter: &mut Formatter<'_>) -> fmt::Result {
33 write!(
34 formatter,
35 "map failed at index {}: {}",
36 self.index, self.message
37 )
38 }
39}
40
41impl std::error::Error for MapError {}
42
43pub(crate) fn truncate_message(mut message: String) -> String {
44 if message.len() > MAX_ERROR_MESSAGE_BYTES {
45 let mut end = MAX_ERROR_MESSAGE_BYTES;
46 while !message.is_char_boundary(end) {
47 end -= 1;
48 }
49 message.truncate(end);
50 }
51 message
52}
53
54pub fn map<T, U, E, F>(items: Vec<T>, mut f: F) -> Result<Vec<U>, MapError>
74where
75 F: FnMut(T) -> Result<U, E>,
76 E: Display,
77{
78 let mut output = Vec::with_capacity(items.len());
79 for (index, item) in items.into_iter().enumerate() {
80 match f(item) {
81 Ok(value) => output.push(value),
82 Err(error) => return Err(MapError::new(index, error)),
83 }
84 }
85 Ok(output)
86}
87
88#[cfg(test)]
89mod tests;