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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
//! multi-print - Extension Trait for convenient string value output multiplication
//! 
//! This is a basic crate that provides a string multiplier Trait and implements it 
//! for Rust's `std::str::String`. 
//! 
//! If you wish to implement the `MultiPrint` trait for your types, you will need 
//! to ensure that those types support the `ToString` trait which is implemented 
//! by default when you implement `std::fmt::Display`.
//! 
//! An example of using the crate:
//! 
//! ```
//! use multiprint::{MultiPrint, Decorate};
//! 
//! fn main() {
//!     let s = String::from("Echo..");
//!     println!("{}", s.times(5, ' '));
//! 
//!     assert_eq!(s.times(5, ' '), "Echo.. Echo.. Echo.. Echo.. Echo..");
//!     assert_eq!("Hello!".to_string().times(2, ' '), "Hello! Hello!");
//! 
//!     // this will output:
//!     //
//!     //   Header 1
//!     //   --------
//!     //
//!     println!("{}", "Header 1".to_string().underline('-'));
//! 
//!     // this will output:
//!     //
//!     //   --------
//!     //   Header 1
//!     //
//!     println!("{}", "Header 1".to_string().overline('-'));
//! 
//!     // this will output:
//!     //
//!     //   ________
//!     //   Header 1
//!     //   ========
//!     //
//!     println!("{}", "Header 1".to_string().outline('_', '='));
//! 
//!     // this will output:
//!     //
//!     //   ------------
//!     //   | Header 1 |
//!     //   ------------
//!     //
//!     println!("{}", "Header 1".to_string().border('-', '|'));
//!     
//! }
//! ```
use std::string::ToString;

/// This MultiPrint trait exposes a default implementation of the `times()` method
/// 
/// By default, the `times` method is auto-implemented for the `String` type
pub trait MultiPrint : ToString {
    fn times(&self, n: usize, sep: char) -> String {
        let mut str = String::new();
        for i in 0..n {
            if i < n && i > 0 {
                str.push(sep);
            }
            str.push_str(&self.to_string());
        }
        str
    }
}

/// The `Decorate` trait exposes methods for decorating text with underlines, overlines and outlines 
/// 
/// By default, the `Decorate` trait methods are automatically implemented for the `String` type
pub trait Decorate : ToString {
    /// Render the string with an underline on the next line using the specific character
    fn underline(&self, underline_character: char) -> String;

    /// Render the string with an overline on the previous line using the specific character
    fn overline(&self, overline_character: char) -> String;

    /// Render the string with both an over- and under-line on the previous and following lines using the specific characters
    fn outline(&self, overline_character: char, underline_character: char) -> String;

    /// Render the string with a complete border around it
    fn border(&self, line_character: char, side_character: char) -> String;
}

impl MultiPrint for String {}

impl Decorate for String {
    fn underline(&self, underline_character: char) -> String {
        format!("{}\n{}", self.to_string(), std::iter::repeat(underline_character).take(self.len()).collect::<String>())
    }

    fn overline(&self, overline_character: char) -> String {
        format!("{}\n{}", std::iter::repeat(overline_character).take(self.len()).collect::<String>(), self.to_string())
    }

    fn outline(&self, overline_character: char, underline_character: char) -> String {
        format!("{}\n{}\n{}", 
            std::iter::repeat(overline_character).take(self.len()).collect::<String>(), 
            self.to_string(),
            std::iter::repeat(underline_character).take(self.len()).collect::<String>()
        )
    }

    fn border(&self, line_character: char, side_character: char) -> String {
        format!("{}\n{} {} {}\n{}",
            std::iter::repeat(line_character).take(self.len()+4).collect::<String>(), 
            side_character,
            self.to_string(),
            side_character,
            std::iter::repeat(line_character).take(self.len()+4).collect::<String>()
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    #[test]
    fn hello_twice() {
        let s = String::from("Hello");

        assert_eq!(s.times(2, ' '), "Hello Hello");
    }

    #[test]
    fn do_it_5_times() {
        let s = String::from("Peter");

        assert_eq!(s.times(5, '-'), "Peter-Peter-Peter-Peter-Peter");
    }

    #[test]
    fn underline_test() {
        let s = String::from("This is a header");

        assert_eq!(s.underline('-'), "This is a header\n----------------");
    }

    #[test]
    fn overline_test() {
        let s = String::from("This is a header");

        assert_eq!(s.overline('-'), "----------------\nThis is a header");
    }

    #[test]
    fn outline() {
        let s = String::from("This is a header");

        assert_eq!(s.outline('-', '='), "----------------\nThis is a header\n================");
    }

    #[test]
    fn border() {
        let s = String::from("Bordered");

        assert_eq!(s.border('-', '|'), "------------\n| Bordered |\n------------");
    }
}