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
//! This module contains a Margin settings of a [`Table`].
//!
//! # Example
//!
#![cfg_attr(feature = "std", doc = "```")]
#![cfg_attr(not(feature = "std"), doc = "```ignore")]
//! use tabled::{settings::{Margin, Style}, Table};
//!
//! let data = vec!["Hello", "World", "!"];
//!
//! let mut table = Table::new(data);
//! table.with(Style::markdown()).with(Margin::new(3, 3, 1, 0));
//!
//! assert_eq!(
//!     table.to_string(),
//!     concat!(
//!         "               \n",
//!         "   | &str  |   \n",
//!         "   |-------|   \n",
//!         "   | Hello |   \n",
//!         "   | World |   \n",
//!         "   | !     |   ",
//!     )
//! );
//! ```
//!
//! [`Table`]: crate::Table

use crate::{
    grid::{
        ansi::ANSIStr,
        config::{CompactConfig, CompactMultilineConfig},
        config::{Indent, Sides},
    },
    settings::TableOption,
};

#[cfg(feature = "std")]
use crate::grid::{ansi::ANSIBuf, config::ColoredConfig};

/// Margin is responsible for a left/right/top/bottom outer indent of a grid.
///
#[cfg_attr(feature = "std", doc = "```")]
#[cfg_attr(not(feature = "std"), doc = "```ignore")]
/// # use tabled::{settings::Margin, Table};
/// # let data: Vec<&'static str> = Vec::new();
/// let table = Table::new(&data)
///     .with(Margin::new(1, 1, 1, 1).fill('>', '<', 'V', '^'));
/// ```
#[derive(Debug, Clone)]
pub struct Margin<C = ANSIStr<'static>> {
    indent: Sides<Indent>,
    colors: Option<Sides<C>>,
}

impl Margin {
    /// Construct's an Margin object.
    ///
    /// It uses space(' ') as a default fill character.
    /// To set a custom character you can use [`Margin::fill`] function.
    pub const fn new(left: usize, right: usize, top: usize, bottom: usize) -> Self {
        Self {
            indent: Sides::new(
                Indent::spaced(left),
                Indent::spaced(right),
                Indent::spaced(top),
                Indent::spaced(bottom),
            ),
            colors: None,
        }
    }
}

impl<Color> Margin<Color> {
    /// The function, sets a characters for the margin on an each side.
    pub const fn fill(mut self, left: char, right: char, top: char, bottom: char) -> Self {
        self.indent.left.fill = left;
        self.indent.right.fill = right;
        self.indent.top.fill = top;
        self.indent.bottom.fill = bottom;
        self
    }

    /// The function, sets a characters for the margin on an each side.
    pub fn colorize<C>(self, left: C, right: C, top: C, bottom: C) -> Margin<C> {
        Margin {
            indent: self.indent,
            colors: Some(Sides::new(left, right, top, bottom)),
        }
    }
}

#[cfg(feature = "std")]
impl<R, D, C> TableOption<R, ColoredConfig, D> for Margin<C>
where
    C: Into<ANSIBuf> + Clone,
{
    fn change(self, _: &mut R, cfg: &mut ColoredConfig, _: &mut D) {
        let indent = self.indent;
        let margin = Sides::new(indent.left, indent.right, indent.top, indent.bottom);
        cfg.set_margin(margin);

        if let Some(colors) = &self.colors {
            let margin = Sides::new(
                Some(colors.left.clone().into()),
                Some(colors.right.clone().into()),
                Some(colors.top.clone().into()),
                Some(colors.bottom.clone().into()),
            );
            cfg.set_margin_color(margin);
        }
    }
}

impl<R, D, C> TableOption<R, CompactConfig, D> for Margin<C>
where
    C: Into<ANSIStr<'static>> + Clone,
{
    fn change(self, _: &mut R, cfg: &mut CompactConfig, _: &mut D) {
        *cfg = cfg.set_margin(self.indent);

        if let Some(c) = self.colors {
            // todo: make a new method (BECAUSE INTO doesn't work) try_into();
            let colors = Sides::new(c.left.into(), c.right.into(), c.top.into(), c.bottom.into());
            *cfg = cfg.set_margin_color(colors);
        }
    }
}

impl<R, D, C> TableOption<R, CompactMultilineConfig, D> for Margin<C>
where
    C: Into<ANSIStr<'static>> + Clone,
{
    fn change(self, _: &mut R, cfg: &mut CompactMultilineConfig, _: &mut D) {
        cfg.set_margin(self.indent);

        if let Some(c) = self.colors {
            // todo: make a new method (BECAUSE INTO doesn't work) try_into();
            let colors = Sides::new(c.left.into(), c.right.into(), c.top.into(), c.bottom.into());
            cfg.set_margin_color(colors);
        }
    }
}