tabled/settings/formatting/
charset.rs

1use std::borrow::Cow;
2
3use crate::{
4    grid::config::{Entity, Position},
5    grid::records::{ExactRecords, PeekableRecords, Records, RecordsMut},
6    settings::{CellOption, TableOption},
7};
8
9/// A structure to handle special chars.
10#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
11pub struct Charset;
12
13impl Charset {
14    /// Returns [`CleanCharset`] which removes all `\t` and `\r` occurrences.
15    ///
16    /// Notice that tab is just removed rather then being replaced with spaces.
17    /// You might be better call [`TabSize`] first if you not expect such behavior.
18    ///
19    /// # Example
20    ///
21    /// ```
22    /// use tabled::{Table, settings::formatting::Charset};
23    ///
24    /// let text = "Some\ttext\t\twith \\tabs";
25    ///
26    /// let mut table = Table::new([text]);
27    /// table.with(Charset::clean());
28    ///
29    /// assert_eq!(
30    ///     table.to_string(),
31    ///     "+--------------------+\n\
32    ///      | &str               |\n\
33    ///      +--------------------+\n\
34    ///      | Sometextwith \\tabs |\n\
35    ///      +--------------------+"
36    /// )
37    /// ```
38    ///
39    /// [`TabSize`]: crate::settings::formatting::TabSize
40    pub fn clean() -> CleanCharset {
41        CleanCharset
42    }
43}
44
45/// [`CleanCharset`] removes all `\t` and `\r` occurrences.
46///
47/// # Example
48///
49/// ```
50/// use std::iter::FromIterator;
51/// use tabled::{
52///     Table, builder::Builder,
53///     settings::formatting::Charset,
54/// };
55///
56/// let text = "Some text which was created on windows \r\n yes they use this \\r\\n";
57///
58/// let mut builder = Builder::from(Table::from_iter([[text]]));
59/// builder.insert_record(0, ["win. text"]);
60///
61/// let mut table = builder.build();
62/// table.with(Charset::clean());
63///
64/// assert_eq!(
65///     table.to_string(),
66///     "+-----------------------------------------+\n\
67///      | win. text                               |\n\
68///      +-----------------------------------------+\n\
69///      | Some text which was created on windows  |\n\
70///      |  yes they use this \\r\\n                 |\n\
71///      +-----------------------------------------+"
72/// )
73/// ```
74#[derive(Debug, Default, Clone)]
75pub struct CleanCharset;
76
77impl CleanCharset {
78    /// Removes all symbols which may break the layout such as `\t`, `\r` and more.
79    ///
80    /// Notice that tab is just removed rather then being replaced with spaces.
81    ///
82    /// # Example
83    ///
84    /// ```
85    /// use tabled::settings::formatting::CleanCharset;
86    ///
87    /// assert_eq!(
88    ///     CleanCharset::clean("Some\ttext\t\twith \\tabs\r\nSome"),
89    ///     "Sometextwith \\tabs\nSome"
90    /// )
91    /// ```
92    pub fn clean(s: &str) -> Cow<'_, str> {
93        Cow::Owned(clean_charset(s))
94    }
95}
96
97impl<R, D, C> TableOption<R, C, D> for CleanCharset
98where
99    R: Records + ExactRecords + RecordsMut<String> + PeekableRecords,
100{
101    fn change(self, records: &mut R, _: &mut C, _: &mut D) {
102        // TODO: Add a grid iterator which produces POS to squash these for loops
103
104        for row in 0..records.count_rows() {
105            for col in 0..records.count_columns() {
106                let pos = Position::new(row, col);
107                let text = records.get_text(pos);
108                let text = clean_charset(text);
109                records.set(pos, text);
110            }
111        }
112    }
113}
114
115impl<R, C> CellOption<R, C> for CleanCharset
116where
117    R: Records + ExactRecords + PeekableRecords + RecordsMut<String>,
118{
119    fn change(self, records: &mut R, _: &mut C, entity: Entity) {
120        let count_rows = records.count_rows();
121        let count_cols = records.count_columns();
122        for pos in entity.iter(count_rows, count_cols) {
123            let text = records.get_text(pos);
124            let text = clean_charset(text);
125            records.set(pos, text);
126        }
127    }
128}
129
130fn clean_charset(text: &str) -> String {
131    // It's enough for covering '\t' and '\r'
132    // as well as a list of other unwanted escapes.
133    text.replace(|c| c != '\n' && c < ' ', "")
134}