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
use std::fs::File;
use std::io::{self, Error, Read, Write};
use std::path::Path;

/// Informs whether a read was successfull or not.
///
/// After a read try, return the content or an error explaining what went wrong.
/// The content is of type `Vec<u8>`.
pub type ReadResult = Result<Vec<u8>, Error>;

/// Informs whether a write was successfull or not.
///
/// A wraper around `io::Error`. The `Ok` value isn't used.
pub type WriteResult = Result<(), Error>;

/// Takes a slice of `u8` (chars) and parses it into a number.
///
/// It uses the same approach as C: if the next character represents a number,
/// then parse it; if not, stop the algorithm.
fn atoi(chars: &[u8]) -> u64 {
    chars.iter()
        .take_while(|&n| 48 <= *n && *n <= 57)
        .fold(0, |acc, &n| (acc * 10) + (n - 48) as u64)
}

/// Convert a vector of bytes into a vector of numbers.
///
/// Splits a vector of `u8` by spaces (the value `32` in ASCII) into many groups
/// of bytes and then convert those groups into numbers.
///
/// # Examples
///
/// ```
/// use kripher::utils::split_into_numbers;
///
/// let chars = vec![49, 49, 32, 52, 50];
/// let result = vec![11, 42];
///
/// assert_eq!(split_into_numbers(chars), result);
/// ```
pub fn split_into_numbers(chars: Vec<u8>) -> Vec<u64> {
    chars.split(|&c| c == 32)
        .filter(|s| !s.is_empty())
        .map(atoi)
        .collect::<Vec<u64>>()
}

/// Reads a `Read` to a vector of `u8`.
///
/// If any error is found, return an `Err`.
fn read<T: Read>(mut reader: T) -> ReadResult {
    let mut res = Vec::new();

    match reader.read_to_end(&mut res) {
        Ok(_) => Ok(res), // Return the vector.
        Err(e) => Err(e), // Build a new error, of type ReadResult.
    }
}

/// Reads from a file or from stdin.
///
/// If `file` is `Some`, then use it as a filename to read from, Otherwise, read
/// from the standard input.
///
/// If any error is found, return an `Err`.
///
/// # Examples
///
/// ```
/// use kripher::utils::read_from;
///
/// // Read from standard input.
/// match read_from::<String>(None) {
///     Ok(_) => println!("Read correctly!"),
///     Err(_) => println!("Oops!"),
/// }
/// ```
pub fn read_from<P>(file: Option<P>) -> ReadResult
    where P: AsRef<Path>
{
    match file {
        Some(file) => File::open(file).and_then(read),
        None => read(io::stdin()),
    }
}

/// Writes a `String` to a `Write`.
fn write<T: Write>(mut write: T, content: String) -> WriteResult {
    write.write_all(content.as_bytes())
}

/// Writes `content` to a file or to stdout.
///
/// If `file` is `Some`, then use it as a filename to write to. Otherwise, write
/// to the standard output.
///
/// If any error is found, return an `Err`.
///
/// # Examples
///
/// ```
/// use kripher::utils::write_to;
///
/// // Write 'Content' to a file.
/// match write_to(Some("/tmp/output"), String::from("Content")) {
///     Ok(_) => println!("write successfull"),
///     Err(_) => println!("Oops!"),
/// }
/// ```
pub fn write_to<P>(file: Option<P>, content: String) -> WriteResult
    where P: AsRef<Path>
{
    match file {
        Some(file) => File::create(file).and_then(|f| write(f, content)),
        None => write(io::stdout(), content),
    }
}

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

    fn chars(numbers: &str) -> Vec<u8> {
        numbers.chars()
            .map(|c| c as u8)
            .collect::<Vec<u8>>()
    }

    #[test]
    fn test_starting_zero() {
        let bytes = chars("01 002 0030");

        assert_eq!(split_into_numbers(bytes), vec![1, 2, 30]);
    }

    #[test]
    fn test_middle_letter() {
        let bytes = chars("435a 33ss56 aa");

        assert_eq!(split_into_numbers(bytes), vec![435, 33, 0]);
    }

    #[test]
    fn test_multiple_spaces() {
        let bytes = chars("45  545     76");

        assert_eq!(split_into_numbers(bytes), vec![45, 545, 76]);
    }
}