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
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
//! The `wbuf` crate unifies standard IO, memory and file buffers into a unified type, allowing
//! to effectively leave the type of buffer used to the user.
//!
//! # How to use
//!
//! The `buffers` crate exposes three types; one for input, one for output, and one for duplex in/out
//! operations. For convenience, each type has a `from_arg` constructor that takes in the output of
//! a commandline parser (such as `clap`) and returns the buffer of the appropriate type (see the
//! function docs for more details).
//!
//! IO Read/Write traits are implemented for the types meaning you can use those wrapper types as a
//! drop-in replacement of "regular" buffers.
//!
//! # Example
//!
//! ```rust
//! use clap::{App, Arg};
//! use wbuf::{Input, Output};
//! let matches = App::new("app")
//!     .arg(Arg::with_name("input").index(1))
//!     .arg(Arg::with_name("output").index(2))
//!     .get_matches();
//! let mut input_buf = Input::from_arg(matches.value_of("input"));
//! let mut output_buf = Output::from_arg(matches.value_of("output"));
//! parse_input(&mut input_buf).and_then(|ast| transpile(ast, &mut output_buf));
//! ```

use std::{fs, io};
use std::io::{Cursor, Error, Read, Write};

/// Input buffer wrapper type. Wraps stdin, a read-only memory Cursor, or a readable file buffer.
pub enum Input {
    Standard(io::Stdin),
    Memory(io::Cursor<Vec<u8>>),
    File(fs::File),
}

/// Output buffer wrapper type. Wraps stdout, a write-only memory Cursor, or a writeable file buffer.
pub enum Output {
    Standard(io::Stdout),
    Memory(io::Cursor<Vec<u8>>),
    File(fs::File),
}

/// Duplex I/O buffer wrapper type. Wraps stdin/stdout, a read/write Cursor, or a readable/writable
/// file buffer.
pub enum InputOutput {
    Standard(io::Stdin, io::Stdout),
    Memory(io::Cursor<Vec<u8>>),
    File(fs::File),
}

impl Input {
    /// Returns an Input wrapping stdin.
    pub fn stdin() -> Self {
        Input::Standard(io::stdin())
    }

    /// Returns an Input wrapping a Cursor.
    pub fn memory() -> Self {
        Input::Memory(Cursor::new(vec![]))
    }

    /// Returns an Input wrapping a file.
    pub fn file(path: &str) -> io::Result<Self> {
        fs::OpenOptions::new()
            .read(true)
            .open(path)
            .map(Input::File)
    }

    /// Returns either a wrapped file buffer, or stdin, depending on the argument passed in.
    ///
    /// The function selects the buffer following these rules:
    /// - No value, or the a literal "-" returns stdin.
    /// - Any other value returns a wrapped file buffer. The file is opened with std::fs::OpenOptions,
    ///  therefore the file is required to exist and be readable for the operation to succeed.
    pub fn from_arg(arg: Option<&str>) -> io::Result<Self> {
        match arg {
            None | Some("-") => Ok(Self::stdin()),
            Some(fname) => Self::file(fname),
        }
    }
}

impl Read for Input {
    /// Reads from the underlying buffer.
    fn read(&mut self, buf: &mut [u8]) -> io::Result<usize> {
        match self {
            Input::Standard(ref mut s) => s.read(buf),
            Input::Memory(ref mut m) => m.read(buf),
            Input::File(ref mut f) => f.read(buf),
        }
    }
}

impl Output {
    /// Returns an Output wrapping stdout.
    pub fn stdout() -> Self {
        Output::Standard(io::stdout())
    }

    /// Returns an Output wrapping a Cursor.
    pub fn memory() -> Self {
        Output::Memory(Cursor::new(vec![]))
    }

    /// Returns an Output wrapping a writeable file.
    pub fn file(path: &str) -> io::Result<Self> {
        fs::OpenOptions::new()
            .write(true)
            .open(path)
            .map(Output::File)
    }

    /// Returns either a wrapped file buffer, or stdin, depending on the argument passed in.
    ///
    /// The function selects the buffer following these rules:
    /// - No value, or the a literal "-" returns stdin.
    /// - Any other value returns a wrapped file buffer. The file is opened with std::fs::OpenOptions,
    ///  therefore the parent folder (or the file itself, if it already exists) is required to be
    /// writable for the operation to succeed.
    pub fn from_arg(arg: Option<&str>) -> io::Result<Self> {
        match arg {
            None | Some("-") => Ok(Self::stdout()),
            Some(fname) => Self::file(fname),
        }
    }
}

impl Write for Output {
    /// Writes data into the underlying buffer.
    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        match self {
            Output::Standard(ref mut s) => s.write(buf),
            Output::Memory(ref mut m) => m.write(buf),
            Output::File(ref mut f) => f.write(buf),
        }
    }

    /// Flushes the buffer.
    fn flush(&mut self) -> Result<(), Error> {
        match self {
            Output::Standard(ref mut s) => s.flush(),
            Output::Memory(ref mut m) => m.flush(),
            Output::File(ref mut f) => f.flush(),
        }
    }
}

impl InputOutput {
    /// Returns an InputOutput wrapping stdin and stdout.
    pub fn stdio() -> InputOutput {
        InputOutput::Standard(io::stdin(), io::stdout())
    }

    /// Returns an InputOutput wrapping a Cursor.
    pub fn memory() -> InputOutput {
        InputOutput::Memory(Cursor::new(vec![]))
    }

    /// Returns an InputOutput wrapping a readable and writable file.
    pub fn file(path: &str) -> io::Result<InputOutput> {
        fs::OpenOptions::new().read(true).write(true).open(path).map(InputOutput::File)
    }

    /// Returns either a wrapped file buffer, or stdin, depending on the argument passed in.
    ///
    /// The function selects the buffer following these rules:
    /// - No value, or the a literal "-" returns stdin.
    /// - Any other value returns a wrapped file buffer. The file is opened with std::fs::OpenOptions,
    ///  therefore the file is required to exist, and be readable *and* writable for the operation
    /// to succeed.
    pub fn from_arg(arg: Option<&str>) -> io::Result<InputOutput> {
        match arg {
            None | Some("-") => Ok(Self::stdio()),
            Some(path) => Self::file(path),
        }
    }
}

impl Read for InputOutput {
    /// Read from the underlying buffer.
    fn read(&mut self, buf: &mut [u8]) -> Result<usize, Error> {
        match self {
            InputOutput::Standard(stdin, _) => stdin.read(buf),
            InputOutput::Memory(c) => c.read(buf),
            InputOutput::File(f) => f.read(buf)
        }
    }
}

impl Write for InputOutput {
    /// Writes into the underlying buffer.
    fn write(&mut self, buf: &[u8]) -> Result<usize, Error> {
        match self {
            InputOutput::Standard(_, stdout) => stdout.write(buf),
            InputOutput::Memory(c) => c.write(buf),
            InputOutput::File(f) => f.write(buf),
        }
    }

    /// Flushes the underlying buffer.
    fn flush(&mut self) -> Result<(), Error> {
        match self {
            InputOutput::Standard(_, stdout) => stdout.flush(),
            InputOutput::Memory(m) => m.flush(),
            InputOutput::File(f) => f.flush()
        }
    }
}