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
//! Padding utilities

use crate::formatter::get_length;
use std::fmt;
use std::fmt::Write;

pub struct PaddingEntry {
    pub text: String,
    pub width: usize,
    pub alignment: Alignment,
}

pub enum Alignment {
    Left,
    Center,
    Right,
}

pub fn align(
    entries: &Vec<PaddingEntry>,
    max_width: usize,
    pad_char: char,
    mut func: impl FnMut(String) -> fmt::Result,
) -> fmt::Result {
    let mut offset = 0;
    let mut line = String::new();
    for entry in entries {
        let text = &entry.text;
        let length = get_length(text);
        if length > entry.width {
            offset += length - entry.width;
            line.push_str(text);
        } else {
            let mut padding_size = entry.width - length;

            // try to correct the offset
            let offset_amount = offset.clamp(0, padding_size);
            offset -= offset_amount;
            padding_size -= offset_amount;

            let padding = pad_char.to_string().repeat(padding_size);
            match entry.alignment {
                Alignment::Left => {
                    write!(&mut line, "{text}{padding}")?;
                }
                Alignment::Center => {
                    let (left_padding, right_padding) = padding.split_at(padding.len() / 2);
                    write!(&mut line, "{left_padding}{text}{right_padding}")?;
                }
                Alignment::Right => {
                    write!(&mut line, "{padding}{text}")?;
                }
            }
        }
    }

    if get_length(&line) > max_width {
        let mut line_builder = String::new();
        for ch in line.chars() {
            line_builder.push(ch);
            if get_length(&line_builder) >= max_width {
                func(line_builder)?;
                line_builder = String::new();
            }
        }

        if !line_builder.is_empty() {
            func(line_builder)?;
        }
    } else {
        func(line)?;
    }

    Ok(())
}