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
extern crate smallvec;

use smallvec::*;
use std::io::{ Write, Result };
use std::iter::repeat;

// Constants! :D
const ENDSL:   &[u8] = b"| ";
const ENDSR:   &[u8] = b" |\n";
#[cfg(not(feature = "clippy"))]
const FERRIS:  &[u8] = br#"
              \
               \
                  _~^~^~_
              \) /  o o  \ (/
                '_   -   _'
                / '-----' \
"#;
#[cfg(feature = "clippy")]
const CLIPPY:  &[u8] = br#"
              \
               \
                  __
                 /  \
                 |  |
                 @  @
                 |  |
                 || |/
                 || ||
                 |\_/|
                 \___/
"#;
const NEWLINE: u8 = '\n' as u8;
const SPACE:   u8  = ' ' as u8;
const DASH:    u8  = '-' as u8;

// A decent number for SmallVec's Buffer Size, not too large
// but also big enough for most inputs
const BUFSIZE: usize = 2048;

// We need a value to add to the width which includes
// the length of ENDSL and ENDSR for the proper size
// calculation of the bar at the top and bottom of the
// box
const OFFSET:  usize = 4;


/// Have Ferris print out her saying something.
///
/// `input` is a slice of bytes that you want to be written out to somewhere
///
/// `width` is how wide you want the box that Ferris says something into to be
///
/// `writer` is anywhere that can be written to using the Writer trait like
/// STDOUT or STDERR
///
/// # Example
///
/// The following bit of code will write the byte string to STDOUT
///
/// ```rust
/// use ferris_says::*;
/// use std::io::{ stdout, BufWriter };
///
/// let stdout = stdout();
/// let out = b"Hello fellow Rustaceans!";
/// let width = 24;
///
/// let mut writer = BufWriter::new(stdout.lock());
/// say(out, width, &mut writer).unwrap();
/// ```
///
/// This will print out:
///
/// ```plain
/// ----------------------------
/// | Hello fellow Rustaceans! |
/// ----------------------------
///               \
///                \
///                   _~^~^~_
///               \) /  o o  \ (/
///                 '_   -   _'
///                 / '-----' \
/// ```

pub fn say<W>(input: &[u8], width: usize, writer: &mut W) -> Result<()>
    where W: Write
{
    // Final output is stored here
    let mut write_buffer = SmallVec::<[u8; BUFSIZE]>::new();

    // The top and bottom bar for the text box is calculated once here
    let bar_buffer: Vec<u8> = repeat(DASH).take(width + OFFSET).collect();

    write_buffer.extend_from_slice(&bar_buffer);
    write_buffer.push(NEWLINE);
    for i in input.split(|x| *x == '\n' as u8) {
        for j in i.chunks(width) {
            write_buffer.extend_from_slice(ENDSL);
            write_buffer.extend_from_slice(j);

            for _ in 0 .. width - j.len() {
                write_buffer.push(SPACE);
            }

            write_buffer.extend_from_slice(ENDSR);
        }

    }
    write_buffer.extend_from_slice(&bar_buffer);
    #[cfg(feature = "clippy")]
    write_buffer.extend_from_slice(CLIPPY);
    #[cfg(not(feature = "clippy"))]
    write_buffer.extend_from_slice(FERRIS);
    writer.write_all(&write_buffer)?;

    Ok(())
}