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
use std::fmt;
use std::fmt::Write;

use regex::Regex;

use crate::bracket::br;
use crate::chars::{LF, SP, TB, VO};
use crate::enums::Brac;

lazy_static! {
    static ref LINEFEED: Regex = Regex::new(r"\n").unwrap();
    static ref COMMA: Regex = Regex::new(r",").unwrap();
}

pub trait Concat: IntoIterator {
    fn join(self, delim: &str) -> String where Self: Sized,
                                               Self::IntoIter: Iterator<Item=Self::Item>,
                                               Self::Item: fmt::Display
    {
        let mut iter = self.into_iter();
        match iter.next() {
            None => String::new(),
            Some(first) => {
                let (lb, _) = iter.size_hint(); // estimate lower bound of capacity needed
                let mut phrase = String::with_capacity(delim.len() * lb);
                write!(&mut phrase, "{}", first).unwrap();
                iter.for_each(|elt| { write!(&mut phrase, "{}{}", delim, elt).unwrap(); }); // (&mut phrase).push_str(delim);
                phrase
            }
        }
    }
    fn concat_lines(self, delim: &str, level: u8) -> String where Self: Sized,
                                                                  Self::IntoIter: Iterator<Item=Self::Item>,
                                                                  Self::Item: fmt::Display
    {
        let ind = if level > 0 { SP.repeat((level as usize) << 1) } else { String::new() };
        let de = format!("{}{}{}{}", delim, LF, ind, TB);
        let joined = self.into_iter().join(&de);
        return format!("{}{}{}{}", ind, TB, joined, delim);
    }
    fn concat_lines_edged(self, delim: &str, level: u8) -> String where Self: Sized,
                                                                        Self::IntoIter: Iterator<Item=Self::Item>,
                                                                        Self::Item: fmt::Display
    {
        let ind = if level > 0 { SP.repeat((level as usize) << 1) } else { String::new() };
        let de = format!("{}{}{}{}", delim, LF, ind, TB);
        let joined = self.into_iter().join(&de);
        return format!("{}{}{}{}{}{}{}", LF, ind, TB, joined, delim, LF, ind);
    }
    fn contingent_lines(self, delim: &str, level: u8, brac: &Brac) -> String where Self: Sized,
                                                                                   Self::IntoIter: Iterator<Item=Self::Item>,
                                                                                   Self::Item: fmt::Display
    {
        let iter = self.into_iter();
        let edged = match brac { Brac::Nan => false, _ => true };
        let joined: String = if LINEFEED.is_match(delim) {
            let de = if COMMA.is_match(delim) { delim.replace(LF, VO) } else { String::new() };
            if edged {
                iter.concat_lines_edged(&de, level)
            } else {
                iter.concat_lines(&de, level)
            }
        } else {
            iter.join(delim)
        };
        return br(&joined, brac);
    }
}

impl<I> Concat for I where I: IntoIterator {}


pub fn join<I>(vec: I, delim: &str) -> String where I: IntoIterator,
                                                    I: Sized,
                                                    I::IntoIter: Iterator<Item=I::Item>,
                                                    I::Item: fmt::Display
{ vec.join(delim) }

pub fn concat_lines<I>(vec: I, delim: &str, level: u8) -> String where I: IntoIterator,
                                                                       I: Sized,
                                                                       I::IntoIter: Iterator<Item=I::Item>,
                                                                       I::Item: fmt::Display
{ vec.concat_lines(delim, level) }

pub fn concat_lines_edged<I>(vec: I, delim: &str, level: u8) -> String where I: IntoIterator,
                                                                             I: Sized,
                                                                             I::IntoIter: Iterator<Item=I::Item>,
                                                                             I::Item: fmt::Display
{ vec.concat_lines_edged(delim, level) }

pub fn contingent_lines<I>(vec: I, delim: &str, level: u8, brac: &Brac) -> String where I: IntoIterator,
                                                                                        I: Sized,
                                                                                        I::IntoIter: Iterator<Item=I::Item>,
                                                                                        I::Item: fmt::Display
{ vec.contingent_lines(delim, level, brac) }


#[cfg(test)]
mod tests {
    use veho::vector::Mappers;

    use crate::concat::{Concat, contingent_lines};
    use crate::enums::Brac;

    #[test]
    fn test_crostab_simplified() {
        let years = vec!["2004", "1984", "1964"].mapper(String::from);

        println!("years = {}", Concat::join(years.iter(), ", "));
        println!("years = [{}]", (&years).concat_lines(", ", 0));
        println!("years.len = {}", years.len());

        let words = vec!["foo", "bar", "zen"];
        println!("words = [{}]", (&words).concat_lines_edged(", ", 0));
        println!("words.len = {}", words.len());
    }

    #[test]
    fn test_contingent_lines() {
        let years = vec!["2004", "1984", "1964"].mapper(String::from);

        println!("contingent_lines = {}", (&years).contingent_lines(",\n", 0, &Brac::Brk));
        println!("years.len = {}", years.len());
        println!("contingent_lines = {}", (&years).contingent_lines("\n", 0, &Brac::Nan));
        println!("years.len = {}", years.len());
        println!("contingent_lines = {}", contingent_lines(&years, ", ", 0, &Brac::Brk));
        println!("years.len = {}", years.len());
    }

    #[test]
    fn test_regex() {
        use regex::Regex;
        let linefeed = Regex::new(r"\n").unwrap();
        let comma = Regex::new(r",").unwrap();
        let candidates = vec!["shakespeare, william", "william shakespeare", "alpha\nbeta", "alpha beta"];
        candidates.iterate(|x| {
            println!("[{}] contains LINEFEED ({}) contains COMMA ({})", x, linefeed.is_match(x), comma.is_match(x));
        });
    }
}