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
use crate::*;

fn write_iter(
    out: &mut impl Write,
    value: &[Formattable<'_>],
    range: Option<Range>,
    format: Option<&str>,
    join: Option<&str>,
) -> Result {
    if value.is_empty() {
        return Ok(());
    }
    let Range(lhs, inclusive, rhs) = range.unwrap_or(Range(None, false, None));
    let rhs = rhs.unwrap_or(isize::MAX);
    let rhs = (usize::try_from(rhs).unwrap_or(value.len().saturating_sub(rhs.unsigned_abs()))
        + usize::from(inclusive))
    .min(value.len());
    let lhs = lhs.unwrap_or(0);
    let lhs = usize::try_from(lhs)
        .unwrap_or(value.len().saturating_sub(lhs.unsigned_abs()))
        .min(rhs);

    if rhs > lhs {
        if let Some(format) = format {
            for value in &value[lhs..rhs - 1] {
                let mut context = HashMap::new();
                context.insert("", value);
                write(out, format, &context)?;
                if let Some(join) = join {
                    write!(out, "{join}").map_err(|e| Error::Fmt(e, 0))?;
                }
            }
            if let Some(value) = value[..rhs].last() {
                let mut context = HashMap::new();
                context.insert("", value);
                write(out, format, &context)?;
            }
        } else {
            for value in &value[lhs..rhs] {
                write!(
                    out,
                    "{}",
                    value
                        .get_display()
                        .map_err(|e| Error::MissingTraitImpl(e, 0))?
                )
                .map_err(|e| Error::Fmt(e, 0))?;
            }
        }
    }
    Ok(())
}

#[allow(clippy::too_many_arguments)]
pub(crate) fn iter(
    out: &mut impl Write,
    value: &[Formattable<'_>],
    width: Option<usize>,
    precision: Option<usize>,
    alignment: crate::Alignment,
    sign: Sign,
    hash: bool,
    zero: bool,
    range: Option<Range>,
    format: Option<&str>,
    join: Option<&str>,
) -> Result {
    match (precision, sign, hash, zero) {
        (None, Sign::None, false, false) => {
            if let Some(width) = width {
                let mut buf = String::new();
                write_iter(&mut buf, value, range, format, join)?;
                match alignment {
                    Alignment::Left | Alignment::None => write!(out, "{buf:<width$}"),
                    Alignment::Center => write!(out, "{buf:^width$}"),
                    Alignment::Right => write!(out, "{buf:>width$}"),
                }
                .map_err(|e| Error::Fmt(e, 0))
            } else {
                write_iter(out, value, range, format, join)
            }
        }
        _ => Err(ParseError::Iter(0).into()),
    }
}

#[cfg(test)]
mod test {
    use collection_literals::hash;

    use super::*;

    #[test]
    fn iter() {
        let list = &[&1, &5].map(Formattable::display);
        let context = &hash!("h"=> Formattable::iter(list));
        assert_eq!(
            format("{h:i(`{:+05}`)#() )#}", context).unwrap(),
            "`+0001`) `+0005`"
        );
        assert_eq!(format("{h:i(``)}", context).unwrap(), "````");
        assert_eq!(format("{h:i..({})}", context).unwrap(), "15");
        assert_eq!(format("{h:i1..({})}", context).unwrap(), "5");
        assert_eq!(format("{h:i1..1({})}", context).unwrap(), "");
        assert_eq!(format("{h:i2..1({})}", context).unwrap(), "");
        assert_eq!(format("{h:i-1..({})}", context).unwrap(), "5");
        assert_eq!(format("{h:i..-1({})}", context).unwrap(), "1");
        assert_eq!(format("{h:i..-2({})}", context).unwrap(), "");
        assert_eq!(format("{h:i-5..-10({})}", context).unwrap(), "");
        assert_eq!(format("{h:i-1({})}", context).unwrap(), "5");
        assert_eq!(format("{h:i1}", context).unwrap(), "5");
        assert_eq!(format("{h:i..}", context).unwrap(), "15");
        assert_eq!(format("{h:i}", context).unwrap(), "15");
        assert_eq!(format("{h:i..1}", context).unwrap(), "1");
        assert_eq!(format("{h:i1..}", context).unwrap(), "5");
        assert_eq!(format("{h:i..=-1}", context).unwrap(), "15");
    }
}