csv-core 0.1.0

Bare bones CSV parsing with no_std support.
Documentation
use core::str;

use memchr::memchr;

use Terminator;

/// The quoting style to use when writing CSV data.
#[derive(Clone, Copy, Debug)]
pub enum QuoteStyle {
    /// This puts quotes around every field. Always.
    Always,
    /// This puts quotes around fields only when necessary.
    ///
    /// They are necessary when fields contain a quote, delimiter or record
    /// terminator. Quotes are also necessary when writing an empty record
    /// (which is indistinguishable from a record with one empty field).
    ///
    /// This is the default.
    Necessary,
    /// This puts quotes around all fields that are non-numeric. Namely, when
    /// writing a field that does not parse as a valid float or integer, then
    /// quotes will be used even if they aren't strictly necessary.
    NonNumeric,
    /// This *never* writes quotes, even if it would produce invalid CSV data.
    Never,
    /// Hints that destructuring should not be exhaustive.
    ///
    /// This enum may grow additional variants, so this makes sure clients
    /// don't count on exhaustive matching. (Otherwise, adding a new variant
    /// could break existing code.)
    #[doc(hidden)]
    __Nonexhaustive,
}

impl Default for QuoteStyle {
    fn default() -> QuoteStyle {
        QuoteStyle::Necessary
    }
}

/// A builder for configuring a CSV writer.
///
/// This builder permits specifying the CSV delimiter, terminator, quoting
/// style and more.
#[derive(Debug, Default)]
pub struct WriterBuilder {
    wtr: Writer,
}

impl WriterBuilder {
    /// Create a new builder for configuring a CSV writer.
    pub fn new() -> WriterBuilder {
        WriterBuilder { wtr: Writer::default() }
    }

    /// Builder a CSV writer from this configuration.
    pub fn build(&self) -> Writer {
        self.wtr.clone()
    }

    /// The field delimiter to use when writing CSV.
    ///
    /// The default is `b','`.
    pub fn delimiter(&mut self, delimiter: u8) -> &mut WriterBuilder {
        self.wtr.delimiter = delimiter;
        self
    }

    /// The record terminator to use when writing CSV.
    ///
    /// A record terminator can be any single byte. The default is a special
    /// value, `Terminator::CRLF`, which uses `\r\n` as the record terminator.
    ///
    /// The default is `b'\n'`.
    pub fn terminator(&mut self, term: Terminator) -> &mut WriterBuilder {
        self.wtr.term = term;
        self
    }

    /// The quoting style to use when writing CSV.
    ///
    /// By default, this is set to `QuoteStyle::Necessary`, which will only
    /// use quotes when they are necessary to preserve the integrity of data.
    ///
    /// Note that unless the quote style is set to `Never`, an empty field is
    /// quoted if it is the only field in a record.
    pub fn quote_style(&mut self, style: QuoteStyle) -> &mut WriterBuilder {
        self.wtr.style = style;
        self
    }

    /// The quote character to use when writing CSV.
    ///
    /// The default value is `b'"'`.
    pub fn quote(&mut self, quote: u8) -> &mut WriterBuilder {
        self.wtr.quote = quote;
        self
    }

    /// The escape character to use when writing CSV.
    ///
    /// This is only used when `double_quote` is set to `false`.
    ///
    /// The default value is `b'\\'`.
    pub fn escape(&mut self, escape: u8) -> &mut WriterBuilder {
        self.wtr.escape = escape;
        self
    }

    /// The quoting escape mechanism to use when writing CSV.
    ///
    /// When enabled (which is the default), quotes are escaped by doubling
    /// them. e.g., `"` escapes to `""`.
    ///
    /// When disabled, quotes are escaped with the escape character (which
    /// is `\\` by default).
    pub fn double_quote(&mut self, yes: bool) -> &mut WriterBuilder {
        self.wtr.double_quote = yes;
        self
    }
}

/// The result of writing CSV data.
///
/// A value of this type is returned from every interaction with `Writer`. It
/// informs the caller how to proceed, namely, by indicating whether more
/// input should be given (`InputEmpty`) or if a bigger output buffer is needed
/// (`OutputFull`).
#[derive(Clone, Debug, Eq, PartialEq)]
pub enum WriteResult {
    /// This result occurs when all of the bytes from the given input have
    /// been processed.
    InputEmpty,
    /// This result occurs when the output buffer was too small to process
    /// all of the input bytes. Generally, this means the caller must call
    /// the corresponding method again with the rest of the input and more
    /// room in the output buffer.
    OutputFull,
}

/// A writer for CSV data.
///
/// # RFC 4180
///
/// This writer conforms to RFC 4180 with one exception: it doesn't guarantee
/// that all records written are of the same length. Instead, the onus is on
/// the caller to ensure that all records written are of the same length.
#[derive(Clone, Debug)]
pub struct Writer {
    state: WriterState,
    delimiter: u8,
    term: Terminator,
    style: QuoteStyle,
    quote: u8,
    escape: u8,
    double_quote: bool,
}

#[derive(Clone, Debug)]
struct WriterState {
    /// This is set whenever we've begun writing the contents of a field, even
    /// if the contents are empty. We use it to avoid re-computing whether
    /// quotes are necessary.
    in_field: bool,
    /// This is set whenever we've started writing a field that is enclosed in
    /// quotes. When the writer is finished, or if a delimiter or terminator
    /// are written, then a closing quote is inserted when this is true.
    quoting: bool,
    /// The number of total bytes written for the current record.
    ///
    /// If the writer is finished or a terminator is written when this is `0`,
    /// then an empty field is added as a pair of adjacent quotes.
    record_bytes: u64,
}

impl Writer {
    /// Creates a new CSV writer with the default configuration.
    pub fn new() -> Writer {
        Writer::default()
    }

    /// Finish writing CSV data to `output`.
    ///
    /// This must be called when one is done writing CSV data to `output`.
    /// In particular, it will write closing quotes if necessary.
    pub fn finish(&mut self, mut output: &mut [u8]) -> (WriteResult, usize) {
        let mut nout = 0;
        if self.state.record_bytes == 0 && self.state.in_field {
            assert!(!self.state.quoting);
            let (res, o) = self.write(&[self.quote, self.quote], output);
            if o == 0 {
                return (res, 0);
            }
            output = &mut moving(output)[o..];
            nout += o;
            self.state.record_bytes += o as u64;
        }
        if !self.state.quoting {
            return (WriteResult::InputEmpty, nout);
        }
        let (res, o) = self.write(&[self.quote], output);
        if o == 0 {
            return (res, nout);
        }
        nout += o;
        self.state.record_bytes = 0;
        self.state.in_field = false;
        self.state.quoting = false;
        (res, nout)
    }

    /// Write a single CSV field from `input` to `output` while employing this
    /// writer's quoting style.
    ///
    /// This returns the result of writing field data, in addition to the
    /// number of bytes consumed from `input` and the number of bytes
    /// written to `output`.
    ///
    /// The result of writing field data is either `WriteResult::InputEmpty`
    /// or `WriteResult::OutputFull`. The former occurs when all bytes in
    /// `input` were copied to `output`, while the latter occurs when `output`
    /// is too small to fit everything from `input`. The maximum number of
    /// bytes that can be written to `output` is `2 + (2 * input.len())`
    /// because of quoting. (The worst case is a field consisting entirely
    /// of quotes.)
    ///
    /// Multiple successive calls to `field` will write more data to the same
    /// field. Subsequent fields can be written by calling either `delimiter`
    /// or `terminator` first.
    ///
    /// If this writer's quoting style is `QuoteStyle::Necessary`, then `input`
    /// should contain the *entire* field. Otherwise, whether the field needs
    /// to be quoted or not cannot be determined.
    pub fn field(
        &mut self,
        input: &[u8],
        mut output: &mut [u8],
    ) -> (WriteResult, usize, usize) {
        let (mut nin, mut nout) = (0, 0);

        if !self.state.in_field {
            self.state.quoting = self.should_quote(input);
            if self.state.quoting {
                let (res, o) = self.write(&[self.quote], output);
                if o == 0 {
                    return (res, 0, 0);
                }
                output = &mut moving(output)[o..];
                nout += o;
                self.state.record_bytes += o as u64;
            }
            self.state.in_field = true;
        }
        let (res, i, o) =
            if self.state.quoting {
                quote(
                    input, output,
                    self.quote, self.escape, self.double_quote)
            } else {
                write_optimistic(input, output)
            };
        nin += i;
        nout += o;
        self.state.record_bytes += o as u64;
        (res, nin, nout)
    }

    /// Write the configured field delimiter to `output`.
    ///
    /// If the output buffer does not have enough room to fit
    /// a field delimiter, then nothing is written to `output`
    /// and `WriteResult::OutputFull` is returned. Otherwise,
    /// `WriteResult::InputEmpty` is returned along with the number of bytes
    /// written to `output` (which is always `1`).
    pub fn delimiter(
        &mut self,
        mut output: &mut [u8],
    ) -> (WriteResult, usize) {
        let mut nout = 0;
        if self.state.quoting {
            let (res, o) = self.write(&[self.quote], output);
            if o == 0 {
                return (res, o);
            }
            output = &mut moving(output)[o..];
            nout += o;
            self.state.record_bytes += o as u64;
            self.state.quoting = false;
        }
        let (res, o) = self.write(&[self.delimiter], output);
        if o == 0 {
            return (res, nout);
        }
        nout += o;
        self.state.record_bytes += o as u64;
        self.state.in_field = false;
        (res, nout)
    }

    /// Write the configured record terminator to `output`.
    ///
    /// If the output buffer does not have enough room to fit a record
    /// terminator, then no part of the terminator is written and
    /// `WriteResult::OutputFull` is returned. Otherwise,
    /// `WriteResult::InputEmpty` is returned along with the number of bytes
    /// written to `output` (which is always `1` or `2`).
    pub fn terminator(
        &mut self,
        mut output: &mut [u8],
    ) -> (WriteResult, usize) {
        let mut nout = 0;
        if self.state.record_bytes == 0 {
            assert!(!self.state.quoting);
            let (res, o) = self.write(&[self.quote, self.quote], output);
            if o == 0 {
                return (res, 0);
            }
            output = &mut moving(output)[o..];
            nout += o;
            self.state.record_bytes += o as u64;
        }
        if self.state.quoting {
            let (res, o) = self.write(&[self.quote], output);
            if o == 0 {
                return (res, o);
            }
            output = &mut moving(output)[o..];
            nout += o;
            self.state.record_bytes += o as u64;
            self.state.quoting = false;
        }
        let (res, o) = match self.term {
            Terminator::CRLF => write_pessimistic(&[b'\r', b'\n'], output),
            Terminator::Any(b) => write_pessimistic(&[b], output),
        };
        if o == 0 {
            return (res, nout);
        }
        nout += o;
        self.state.record_bytes = 0;
        self.state.in_field = false;
        (res, nout)
    }

    /// Returns true if and only if the given input field *requires* quotes to
    /// preserve the integrity of `input` while taking into account the current
    /// configuration of this writer (except for the configured quoting style).
    fn needs_quotes(&self, input: &[u8]) -> bool {
        input.iter().any(|&b| self.byte_needs_quotes(b))
    }

    fn byte_needs_quotes(&self, b: u8) -> bool {
        self.delimiter == b
        || self.term == b
        || self.quote == b
        // This is a bit hokey. By default, the record terminator is
        // '\n', but we still need to quote '\r' because the reader
        // interprets '\r' as a record terminator by default.
        || b == b'\r' || b == b'\n'
    }

    /// Returns true if and only if we should put the given field data
    /// in quotes. This takes the quoting style into account.
    fn should_quote(&self, input: &[u8]) -> bool {
        match self.style {
            QuoteStyle::Always => true,
            QuoteStyle::Never => false,
            QuoteStyle::NonNumeric => is_non_numeric(input),
            QuoteStyle::Necessary => self.needs_quotes(input),
            _ => unreachable!(),
        }
    }

    fn write(&self, data: &[u8], output: &mut [u8]) -> (WriteResult, usize) {
        if data.len() > output.len() {
            (WriteResult::OutputFull, 0)
        } else {
            output[..data.len()].copy_from_slice(data);
            (WriteResult::InputEmpty, data.len())
        }
    }
}

impl Default for Writer {
    fn default() -> Writer {
        Writer {
            state: WriterState::default(),
            delimiter: b',',
            term: Terminator::Any(b'\n'),
            style: QuoteStyle::default(),
            quote: b'"',
            escape: b'\\',
            double_quote: true,
        }
    }
}

impl Default for WriterState {
    fn default() -> WriterState {
        WriterState {
            in_field: false,
            quoting: false,
            record_bytes: 0,
        }
    }
}

/// Returns true if and only if the given input is non-numeric.
pub fn is_non_numeric(input: &[u8]) -> bool {
    let s = match str::from_utf8(input) {
        Err(_) => return true,
        Ok(s) => s,
    };
    // I suppose this could be faster if we wrote validators of numbers instead
    // of using the actual parser, but that's probably a lot of work for a bit
    // of a niche feature.
    !s.parse::<f64>().is_ok() && !s.parse::<i64>().is_ok()
}

/// Escape quotes `input` and writes the result to `output`.
///
/// If `input` does not have a `quote`, then the contents of `input` are
/// copied verbatim to `output`.
///
/// If `output` is not big enough to store the fully quoted contents of
/// `input`, then `WriteResult::OutputFull` is returned. The `output` buffer
/// will require a maximum of storage of `2 * input.len()` in the worst case
/// (where every byte is a quote).
///
/// In streaming contexts, `quote` should be called in a loop until
/// `WriteResult::InputEmpty` is returned. It is possible to write an infinite
/// loop if your output buffer is less than 2 bytes in length (the minimum
/// storage space required to store an escaped quote).
///
/// In addition to the `WriteResult`, the number of consumed bytes from `input`
/// and the number of bytes written to `output` are also returned.
///
/// `quote` is the quote byte and `escape` is the escape byte. If
/// `double_quote` is true, then quotes are escaped by doubling them,
/// otherwise, quotes are escaped with the `escape` byte.
///
/// N.B. This function is provided for low level usage. It is called
/// automatically if you're using a `Writer`.
pub fn quote(
    mut input: &[u8],
    mut output: &mut [u8],
    quote: u8,
    escape: u8,
    double_quote: bool,
) -> (WriteResult, usize, usize) {
    let (mut nin, mut nout) = (0, 0);
    loop {
        match memchr(quote, input) {
            None => {
                let (res, i, o) = write_optimistic(input, output);
                nin += i;
                nout += o;
                return (res, nin, nout);
            }
            Some(next_quote) => {
                let (res, i, o) = write_optimistic(
                    &input[..next_quote], output);
                input = &input[i..];
                output = &mut moving(output)[o..];
                nin += i;
                nout += o;
                if let WriteResult::OutputFull = res {
                    return (res, nin, nout);
                }
                if double_quote {
                    let (res, o) = write_pessimistic(&[quote, quote], output);
                    if let WriteResult::OutputFull = res {
                        return (res, nin, nout);
                    }
                    nout += o;
                    output = &mut moving(output)[o..];
                } else {
                    let (res, o) = write_pessimistic(&[escape, quote], output);
                    if let WriteResult::OutputFull = res {
                        return (res, nin, nout);
                    }
                    nout += o;
                    output = &mut moving(output)[o..];
                }
                nin += 1;
                input = &input[1..];
            }
        }
    }
}

/// Copy the bytes from `input` to `output`. If `output` is too small to fit
/// everything from `input`, then copy `output.len()` bytes from `input`.
/// Otherwise, copy everything from `input` into `output`.
///
/// In the first case (`output` is too small), `WriteResult::OutputFull` is
/// returned, in addition to the number of bytes consumed from `input` and
/// the number of bytes written to `output`.
///
/// In the second case (`input` is no bigger than `output`),
/// `WriteResult::InputEmpty` is returned, in addition to the number of bytes
/// consumed from `input` and the number of bytes written to `output`.
fn write_optimistic(
    input: &[u8],
    output: &mut [u8],
) -> (WriteResult, usize, usize) {
    if input.len() > output.len() {
        let input = &input[..output.len()];
        output.copy_from_slice(input);
        (WriteResult::OutputFull, output.len(), output.len())
    } else {
        output[..input.len()].copy_from_slice(input);
        (WriteResult::InputEmpty, input.len(), input.len())
    }
}

/// Copy the bytes from `input` to `output` only if `input` is no bigger than
/// `output`. If `input` is bigger than `output`, then return
/// `WriteResult::OutputFull` and copy nothing into `output`. Otherwise,
/// return `WriteResult::InputEmpty` and the number of bytes copied into
/// `output`.
fn write_pessimistic(
    input: &[u8],
    output: &mut [u8],
) -> (WriteResult, usize) {
    if input.len() > output.len() {
        (WriteResult::OutputFull, 0)
    } else {
        output[..input.len()].copy_from_slice(input);
        (WriteResult::InputEmpty, input.len())
    }
}

/// This avoids reborrowing.
/// See: https://bluss.github.io/rust/fun/2015/10/11/stuff-the-identity-function-does/
fn moving<T>(x: T) -> T { x }

#[cfg(test)]
mod tests {
    use writer::{Writer, WriterBuilder, QuoteStyle, quote};
    use writer::WriteResult::*;

    // OMG I HATE BYTE STRING LITERALS SO MUCH.
    fn b(s: &str) -> &[u8] { s.as_bytes() }
    fn s(b: &[u8]) -> &str { ::core::str::from_utf8(b).unwrap() }

    macro_rules! assert_field {
        (
            $wtr:expr, $inp:expr, $out:expr,
            $expect_in:expr, $expect_out:expr,
            $expect_res:expr, $expect_data:expr
        ) => {{
            let (res, i, o) = $wtr.field($inp, $out);
            assert_eq!($expect_res, res, "result");
            assert_eq!($expect_in, i, "input");
            assert_eq!($expect_out, o, "output");
            assert_eq!($expect_data, s(&$out[..o]), "data");
        }}
    }

    macro_rules! assert_write {
        (
            $wtr:expr, $which:ident, $out:expr,
            $expect_out:expr, $expect_res:expr, $expect_data:expr
        ) => {{
            let (res, o) = $wtr.$which($out);
            assert_eq!($expect_res, res, "result");
            assert_eq!($expect_out, o, "output");
            assert_eq!($expect_data, s(&$out[..o]), "data");
        }}
    }

    #[test]
    fn writer_one_field() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
        n += 3;

        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
    }

    #[test]
    fn writer_one_empty_field_terminator() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];

        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
        assert_write!(wtr, finish, &mut out[..], 0, InputEmpty, "");
    }

    #[test]
    fn writer_one_empty_field_finish() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];

        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
        assert_write!(wtr, finish, &mut out[..], 2, InputEmpty, "\"\"");
    }

    #[test]
    fn writer_many_one_empty_field_finish() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];

        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
        assert_write!(wtr, finish, &mut out[..], 2, InputEmpty, "\"\"");
    }

    #[test]
    fn writer_many_one_empty_field_terminator() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];

        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
        assert_field!(wtr, b(""), &mut out[..], 0, 0, InputEmpty, "");
        assert_write!(wtr, terminator, &mut out[..], 3, InputEmpty, "\"\"\n");
        assert_write!(wtr, finish, &mut out[..], 0, InputEmpty, "");
    }

    #[test]
    fn writer_one_field_quote() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(
            wtr, b("a\"bc"), &mut out[n..], 4, 6, InputEmpty, "\"a\"\"bc");
        n += 6;

        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
    }

    #[test]
    fn writer_one_field_stream() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
        n += 3;
        assert_field!(wtr, b("x"), &mut out[n..], 1, 1, InputEmpty, "x");
        n += 1;

        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");
    }

    #[test]
    fn writer_one_field_stream_quote() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(
            wtr, b("abc\""), &mut out[n..], 4, 6, InputEmpty, "\"abc\"\"");
        n += 6;
        assert_field!(wtr, b("x"), &mut out[n..], 1, 1, InputEmpty, "x");
        n += 1;

        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
    }

    #[test]
    fn writer_one_field_stream_quote_partial() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 4];

        assert_field!(wtr, b("ab\"xyz"), out, 2, 3, OutputFull, "\"ab");
        assert_field!(wtr, b("\"xyz"), out, 3, 4, OutputFull, "\"\"xy");
        assert_field!(wtr, b("z"), out, 1, 1, InputEmpty, "z");
        assert_write!(wtr, finish, out, 1, InputEmpty, "\"");
    }

    #[test]
    fn writer_two_fields() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
        n += 3;
        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
        n += 1;
        assert_field!(wtr, b("yz"), &mut out[n..], 2, 2, InputEmpty, "yz");
        n += 2;

        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");

        assert_eq!("abc,yz", s(&out[..n]));
    }

    #[test]
    fn writer_two_fields_non_numeric() {
        let mut wtr = WriterBuilder::new()
            .quote_style(QuoteStyle::NonNumeric)
            .build();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(wtr, b("abc"), &mut out[n..], 3, 4, InputEmpty, "\"abc");
        n += 4;
        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
        n += 2;
        assert_field!(wtr, b("5.2"), &mut out[n..], 3, 3, InputEmpty, "5.2");
        n += 3;
        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
        n += 1;
        assert_field!(wtr, b("98"), &mut out[n..], 2, 2, InputEmpty, "98");
        n += 2;

        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");

        assert_eq!("\"abc\",5.2,98", s(&out[..n]));
    }

    #[test]
    fn writer_two_fields_quote() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(
            wtr, b("a,bc"), &mut out[n..], 4, 5, InputEmpty, "\"a,bc");
        n += 5;
        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
        n += 2;
        assert_field!(wtr, b("\nz"), &mut out[n..], 2, 3, InputEmpty, "\"\nz");
        n += 3;

        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
        n += 1;

        assert_eq!("\"a,bc\",\"\nz\"", s(&out[..n]));
    }

    #[test]
    fn writer_two_fields_two_records() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(wtr, b("abc"), &mut out[n..], 3, 3, InputEmpty, "abc");
        n += 3;
        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
        n += 1;
        assert_field!(wtr, b("yz"), &mut out[n..], 2, 2, InputEmpty, "yz");
        n += 2;
        assert_write!(wtr, terminator, &mut out[n..], 1, InputEmpty, "\n");
        n += 1;
        assert_field!(wtr, b("foo"), &mut out[n..], 3, 3, InputEmpty, "foo");
        n += 3;
        assert_write!(wtr, delimiter, &mut out[n..], 1, InputEmpty, ",");
        n += 1;
        assert_field!(wtr, b("quux"), &mut out[n..], 4, 4, InputEmpty, "quux");
        n += 4;

        assert_write!(wtr, finish, &mut out[n..], 0, InputEmpty, "");

        assert_eq!("abc,yz\nfoo,quux", s(&out[..n]));
    }

    #[test]
    fn writer_two_fields_two_records_quote() {
        let mut wtr = Writer::new();
        let mut out = &mut [0; 1024];
        let mut n = 0;

        assert_field!(
            wtr, b("a,bc"), &mut out[n..], 4, 5, InputEmpty, "\"a,bc");
        n += 5;
        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
        n += 2;
        assert_field!(wtr, b("\nz"), &mut out[n..], 2, 3, InputEmpty, "\"\nz");
        n += 3;
        assert_write!(wtr, terminator, &mut out[n..], 2, InputEmpty, "\"\n");
        n += 2;
        assert_field!(
            wtr, b("f\"oo"), &mut out[n..], 4, 6, InputEmpty, "\"f\"\"oo");
        n += 6;
        assert_write!(wtr, delimiter, &mut out[n..], 2, InputEmpty, "\",");
        n += 2;
        assert_field!(
            wtr, b("quux,"), &mut out[n..], 5, 6, InputEmpty, "\"quux,");
        n += 6;

        assert_write!(wtr, finish, &mut out[n..], 1, InputEmpty, "\"");
        n += 1;

        assert_eq!("\"a,bc\",\"\nz\"\n\"f\"\"oo\",\"quux,\"", s(&out[..n]));
    }

    macro_rules! assert_quote {
        (
            $inp:expr, $out:expr,
            $expect_in:expr, $expect_out:expr,
            $expect_res:expr, $expect_data:expr
        ) => {
            assert_quote!(
                $inp, $out,
                $expect_in, $expect_out, $expect_res, $expect_data,
                true);
        };
        (
            $inp:expr, $out:expr,
            $expect_in:expr, $expect_out:expr,
            $expect_res:expr, $expect_data:expr,
            $double_quote:expr
        ) => {{
            let (res, i, o) = quote($inp, $out, b'"', b'\\', $double_quote);
            assert_eq!($expect_res, res, "result");
            assert_eq!($expect_in, i, "input");
            assert_eq!($expect_out, o, "output");
            assert_eq!(b($expect_data), &$out[..o], "data");
        }}
    }

    #[test]
    fn quote_empty() {
        let inp = b("");
        let out = &mut [0; 1024];

        assert_quote!(inp, out, 0, 0, InputEmpty, "");
    }

    #[test]
    fn quote_no_quotes() {
        let inp = b("foobar");
        let out = &mut [0; 1024];

        assert_quote!(inp, out, 6, 6, InputEmpty, "foobar");
    }

    #[test]
    fn quote_one_quote() {
        let inp = b("\"");
        let out = &mut [0; 1024];

        assert_quote!(inp, out, 1, 2, InputEmpty, r#""""#);
    }

    #[test]
    fn quote_two_quotes() {
        let inp = b("\"\"");
        let out = &mut [0; 1024];

        assert_quote!(inp, out, 2, 4, InputEmpty, r#""""""#);
    }

    #[test]
    fn quote_escaped_one() {
        let inp = b("\"");
        let out = &mut [0; 1024];

        assert_quote!(inp, out, 1, 2, InputEmpty, r#"\""#, false);
    }

    #[test]
    fn quote_escaped_two() {
        let inp = b("\"\"");
        let out = &mut [0; 1024];

        assert_quote!(inp, out, 2, 4, InputEmpty, r#"\"\""#, false);
    }

    #[test]
    fn quote_misc() {
        let inp = b(r#"foo "bar" baz "quux"?"#);
        let out = &mut [0; 1024];

        assert_quote!(
            inp, out, 21, 25, InputEmpty,
            r#"foo ""bar"" baz ""quux""?"#);
    }

    #[test]
    fn quote_stream_no_quotes() {
        let mut inp = b("fooba");
        let out = &mut [0; 2];

        assert_quote!(inp, out, 2, 2, OutputFull, "fo");
        inp = &inp[2..];
        assert_quote!(inp, out, 2, 2, OutputFull, "ob");
        inp = &inp[2..];
        assert_quote!(inp, out, 1, 1, InputEmpty, "a");
    }

    #[test]
    fn quote_stream_quotes() {
        let mut inp = b(r#"a"bc"d""#);
        let out = &mut [0; 2];

        assert_quote!(inp, out, 1, 1, OutputFull, "a");
        inp = &inp[1..];
        assert_quote!(inp, out, 1, 2, OutputFull, r#""""#);
        inp = &inp[1..];
        assert_quote!(inp, out, 2, 2, OutputFull, "bc");
        inp = &inp[2..];
        assert_quote!(inp, out, 1, 2, OutputFull, r#""""#);
        inp = &inp[1..];
        assert_quote!(inp, out, 1, 1, OutputFull, "d");
        inp = &inp[1..];
        assert_quote!(inp, out, 1, 2, InputEmpty, r#""""#);
    }
}