#[cfg(any(feature = "experimental-write", doc))]
use std::io::Write;
use std::io::{BufRead, BufReader, Lines, Read};
use std::marker::PhantomData;
use crate::error::Error;
#[cfg(feature = "experimental-write")]
pub trait WriteFixed {
fn write_fixed<W: Write>(&self, buf: &mut W) -> Result<(), Error>;
}
#[cfg(feature = "experimental-write")]
pub trait WriteFixedAll {
#[cfg_attr(docsrs, doc(cfg(feature = "experimental-write")))]
fn write_fixed_all<W: Write>(self, buf: &mut W) -> Result<(), Error>;
}
#[cfg(feature = "experimental-write")]
impl<T: WriteFixed, Iter: IntoIterator<Item = T>> WriteFixedAll for Iter {
fn write_fixed_all<W: Write>(self, buf: &mut W) -> Result<(), Error> {
for item in self.into_iter() {
item.write_fixed(buf)?;
buf.write("\n".as_bytes())?;
}
Ok(())
}
}
#[derive(Debug)]
pub struct Iter<T, R>
where
T: ReadFixed,
R: Read,
{
failed: bool,
line: usize,
lines: Lines<BufReader<R>>,
t: PhantomData<T>,
}
impl<T: ReadFixed, R: Read> Iter<T, R> {
fn new(read: R) -> Self {
Self {
lines: BufReader::new(read).lines(),
line: 0,
failed: false,
t: PhantomData,
}
}
}
impl<T: ReadFixed, R: Read> Iterator for Iter<T, R> {
type Item = Result<T, Error>;
fn next(&mut self) -> Option<Self::Item> {
if self.failed {
None
} else {
self.line += 1;
match self.lines.next() {
None => None,
Some(Err(e)) => {
self.failed = true;
Some(Err(Error::IoError(e)))
}
Some(Ok(s)) => {
match T::read_fixed_string(s) {
Err(Error::DataError(err)) => {
let err_with_line = err.with_line(self.line);
Some(Err(Error::DataError(err_with_line)))
}
other => Some(other),
}
}
}
}
}
}
pub trait ReadFixed {
fn read_fixed<R>(buf: &mut R) -> Result<Self, Error>
where
Self: Sized,
R: Read;
fn read_fixed_all<R>(buf: R) -> Iter<Self, R>
where
Self: Sized,
R: Read,
{
Iter::new(buf)
}
fn read_fixed_str(s: &str) -> Result<Self, Error>
where
Self: Sized,
{
let mut bytes = s.as_bytes();
Self::read_fixed(&mut bytes)
}
fn read_fixed_string(s: String) -> Result<Self, Error>
where
Self: Sized,
{
let mut bytes = s.as_bytes();
Self::read_fixed(&mut bytes)
}
}
#[cfg(test)]
mod tests {
use fixcol_derive::ReadFixed;
use super::*;
use crate::error::Error;
#[derive(Debug, PartialEq, Eq)]
struct Foo {
foo: String,
}
impl ReadFixed for Foo {
fn read_fixed<R>(buf: &mut R) -> Result<Self, Error>
where
Self: Sized,
R: Read,
{
let mut s: String = String::new();
buf.read_to_string(&mut s)?;
Ok(Self { foo: s })
}
}
#[test]
fn read_fixed_str() {
let foo = Foo::read_fixed_str("bar");
assert_eq!(foo.unwrap(), Foo { foo: "bar".to_string() });
}
#[test]
fn read_fixed_string() {
let s: String = "bar".to_string();
let foo = Foo::read_fixed_string(s);
assert_eq!(foo.unwrap(), Foo { foo: "bar".to_string() });
}
#[test]
fn read_fixed_all() {
let buf = "foo\nbar\nbaz\n";
let expected = vec![
Foo { foo: "foo".to_string() },
Foo { foo: "bar".to_string() },
Foo { foo: "baz".to_string() },
];
let actual: Vec<Foo> = Foo::read_fixed_all(buf.as_bytes())
.map(|r| r.unwrap())
.collect();
assert_eq!(actual, expected);
}
#[test]
fn read_fixed_all_no_trailing() {
let buf = "foo\nbar\nbaz";
let expected = vec![
Foo { foo: "foo".to_string() },
Foo { foo: "bar".to_string() },
Foo { foo: "baz".to_string() },
];
let actual: Vec<Foo> = Foo::read_fixed_all(buf.as_bytes())
.map(|r| r.unwrap())
.collect();
assert_eq!(actual, expected);
}
#[test]
fn iter_debug() {
let buf = "foo\nbar\nbaz";
let iter = Foo::read_fixed_all(buf.as_bytes());
assert_ne!(format!("{:?}", iter), "");
}
#[cfg(feature = "experimental-write")]
fn to_str(inp: Vec<u8>) -> String {
use std::str;
str::from_utf8(inp.as_slice()).unwrap().to_string()
}
#[cfg(feature = "experimental-write")]
use fixcol::WriteFixed;
use crate as fixcol;
#[cfg_attr(feature = "experimental-write", derive(WriteFixed))]
#[derive(ReadFixed, Eq, PartialEq, Debug)]
struct MyStruct {
#[fixcol(width = 10)]
string: String,
#[fixcol(width = 10, align = "right")]
num: i64,
}
#[test]
fn read_struct_derived() {
let expected = MyStruct {
string: "my string".to_string(),
num: 981,
};
let raw = "my string 981";
assert_eq!(raw.len(), 20);
let mut buf = raw.as_bytes();
let actual = MyStruct::read_fixed(&mut buf);
assert_eq!(actual.unwrap(), expected);
}
#[test]
#[cfg(feature = "experimental-write")]
fn write_struct_derived() {
let expected = "my string 981";
assert_eq!(expected.len(), 20);
let my_struct = MyStruct {
string: "my string".to_string(),
num: 981,
};
let mut buf: Vec<u8> = Vec::new();
let res = my_struct.write_fixed(&mut buf);
assert!(res.is_ok());
assert_eq!(to_str(buf), expected);
}
#[cfg_attr(feature = "experimental-write", derive(WriteFixed))]
#[derive(ReadFixed, Eq, PartialEq, Debug)]
#[fixcol(key_width = 2)]
enum MyEnum {
#[fixcol(key = "st")]
Struct {
#[fixcol(width = 10)]
string: String,
#[fixcol(width = 10, align = "right")]
num: i64,
},
#[fixcol(key = "tu")]
Tuple(
#[fixcol(width = 10)] String,
#[fixcol(width = 10, align = "right")] i64,
),
#[fixcol(key = "un")]
Unit,
}
#[test]
fn read_struct_enum_derived() {
let expected = MyEnum::Struct {
string: "my string".to_string(),
num: 981,
};
let raw = "stmy string 981";
assert_eq!(raw.len(), 22);
let mut buf = raw.as_bytes();
let actual = MyEnum::read_fixed(&mut buf);
assert_eq!(actual.unwrap(), expected);
}
#[test]
#[cfg(feature = "experimental-write")]
fn write_struct_enum_derived() {
let expected = "stmy string 981";
assert_eq!(expected.len(), 22);
let my_struct = MyEnum::Struct {
string: "my string".to_string(),
num: 981,
};
let mut buf: Vec<u8> = Vec::new();
let res = my_struct.write_fixed(&mut buf);
assert!(res.is_ok());
assert_eq!(to_str(buf), expected);
}
#[test]
fn read_tuple_enum_derived() {
let expected = MyEnum::Tuple("my string".to_string(), 981);
let raw = "tumy string 981";
assert_eq!(raw.len(), 22);
let mut buf = raw.as_bytes();
let actual = MyEnum::read_fixed(&mut buf);
assert_eq!(actual.unwrap(), expected);
}
#[test]
#[cfg(feature = "experimental-write")]
fn write_tuple_enum_derived() {
let expected = "tumy string 981";
assert_eq!(expected.len(), 22);
let my_struct = MyEnum::Tuple("my string".to_string(), 981);
let mut buf: Vec<u8> = Vec::new();
let res = my_struct.write_fixed(&mut buf);
assert!(res.is_ok());
assert_eq!(to_str(buf), expected);
}
#[test]
fn read_unit_enum_derived() {
let expected = MyEnum::Unit;
let raw = "unmy string 981";
assert_eq!(raw.len(), 22);
let mut buf = raw.as_bytes();
let actual = MyEnum::read_fixed(&mut buf);
assert_eq!(actual.unwrap(), expected);
}
#[test]
#[cfg(feature = "experimental-write")]
fn write_unit_enum_derived() {
let expected = "un";
let my_struct = MyEnum::Unit;
let mut buf: Vec<u8> = Vec::new();
let res = my_struct.write_fixed(&mut buf);
assert!(res.is_ok());
assert_eq!(to_str(buf), expected);
}
}