use std::io;
use std::str;
use serialize::Encodable;
use {ByteString, CsvResult, Encoded, Error};
pub struct Writer<W> {
buf: io::BufferedWriter<W>,
delimiter: u8,
flexible: bool,
crlf: bool,
first_len: uint,
}
pub trait SizedStr for Sized? {
fn as_str_slice<'a>(&'a self) -> &'a str;
}
impl SizedStr for str {
fn as_str_slice<'a>(&'a self) -> &'a str { self }
}
impl SizedStr for String {
fn as_str_slice<'a>(&'a self) -> &'a str { self.as_slice() }
}
impl Writer<io::IoResult<io::File>> {
pub fn from_file(path: &Path) -> Writer<io::IoResult<io::File>> {
Writer::from_writer(io::File::create(path))
}
}
impl<W: io::Writer> Writer<W> {
pub fn encode<E: Encodable<Encoded, Error>>
(&mut self, e: E) -> CsvResult<()> {
let mut erecord = Encoded::new();
try!(e.encode(&mut erecord));
self.write_bytes(erecord.unwrap().into_iter())
}
pub fn write<'a, Sized? S: 'a + SizedStr, I: Iterator<&'a S>>
(&mut self, r: I) -> CsvResult<()> {
self.write_iter(r, |f| Ok(f.as_str_slice().as_bytes()))
}
pub fn write_bytes<S: AsSlice<u8>, I: Iterator<S>>
(&mut self, r: I) -> CsvResult<()> {
self.write_iter(r, |f| Ok(f))
}
#[doc(hidden)]
pub fn write_results<S: AsSlice<u8>, I: Iterator<CsvResult<S>>>
(&mut self, r: I) -> CsvResult<()> {
self.write_iter(r, |f| f)
}
fn write_iter<T, R: AsSlice<u8>, I: Iterator<T>>
(&mut self, r: I, as_sliceable: |T| -> CsvResult<R>)
-> CsvResult<()> {
let mut count = 0;
let delim = self.delimiter;
for (i, field) in r.enumerate() {
count += 1;
if i > 0 {
try!(self.w_bytes(&[delim]));
}
try!(self.w_user_bytes(try!(as_sliceable(field)).as_slice()));
}
try!(self.w_lineterm());
self.set_first_len(count)
}
}
impl<W: io::Writer> Writer<W> {
pub fn from_writer(w: W) -> Writer<W> {
Writer::from_buffer(io::BufferedWriter::new(w))
}
pub fn from_buffer(buf: io::BufferedWriter<W>) -> Writer<W> {
Writer {
buf: buf,
delimiter: b',',
flexible: false,
crlf: false,
first_len: 0,
}
}
pub fn delimiter(mut self, delimiter: u8) -> Writer<W> {
self.delimiter = delimiter;
self
}
pub fn flexible(mut self, yes: bool) -> Writer<W> {
self.flexible = yes;
self
}
pub fn crlf(mut self, yes: bool) -> Writer<W> {
self.crlf = yes;
self
}
pub fn flush(&mut self) -> CsvResult<()> {
self.buf.flush().map_err(Error::Io)
}
}
impl Writer<io::MemWriter> {
pub fn from_memory() -> Writer<io::MemWriter> {
Writer::from_writer(io::MemWriter::new())
}
pub fn as_string<'r>(&'r mut self) -> &'r str {
match self.buf.flush() {
Err(err) => panic!("Error flushing to MemWriter: {}", err),
Ok(()) => str::from_utf8(self.buf.get_ref().get_ref()).unwrap(),
}
}
pub fn as_bytes<'r>(&'r mut self) -> &'r [u8] {
match self.buf.flush() {
Err(err) => panic!("Error flushing to MemWriter: {}", err),
Ok(()) => self.buf.get_ref().get_ref(),
}
}
}
impl<W: io::Writer> Writer<W> {
fn err<S: StrAllocating>(&self, msg: S) -> CsvResult<()> {
Err(Error::Encode(msg.into_string()))
}
fn w_bytes(&mut self, s: &[u8]) -> CsvResult<()> {
self.buf.write(s).map_err(Error::Io)
}
fn w_user_bytes(&mut self, s: &[u8]) -> CsvResult<()> {
let delim = self.delimiter;
let quotable = |&c: &u8| {
c == delim || c == b'\n' || c == b'\r' || c == b'"'
};
if s.is_empty() || s.iter().any(quotable) {
self.w_bytes(quote(s)[])
} else {
self.w_bytes(s)
}
}
fn w_lineterm(&mut self) -> CsvResult<()> {
if self.crlf {
self.w_bytes(b"\r\n")
} else {
self.w_bytes(b"\n")
}
}
fn set_first_len(&mut self, cur_len: uint) -> CsvResult<()> {
if cur_len == 0 {
return self.err("Records must have length greater than 0.")
}
if !self.flexible {
if self.first_len == 0 {
self.first_len = cur_len;
} else if self.first_len != cur_len {
return self.err(format!(
"Record has length {} but other records have length {}",
cur_len, self.first_len))
}
}
Ok(())
}
}
fn quote(mut s: &[u8]) -> ByteString {
let mut buf = Vec::with_capacity(s.len() + 2);
buf.push(b'"');
loop {
match s.position_elem(&b'"') {
None => {
buf.push_all(s);
break
}
Some(next_quote) => {
buf.push_all(s.slice_to(next_quote + 1));
buf.push(b'"');
s = s.slice_from(next_quote + 1);
}
}
}
buf.push(b'"');
ByteString::from_bytes(buf)
}