#![crate_name = "fixed_width"]
#![deny(missing_docs)]
pub use crate::de::{
deserialize, from_bytes, from_bytes_with_fields, from_str, from_str_with_fields,
DeserializeError, Deserializer,
};
pub use crate::{
error::Error,
reader::{ByteReader, Reader, StringReader},
ser::{to_bytes, to_string, to_writer, to_writer_with_fields, SerializeError, Serializer},
writer::{AsByteSlice, Writer},
};
use std::{ops::Range, result};
mod de;
mod error;
mod macros;
mod reader;
mod ser;
mod writer;
pub type Result<T> = result::Result<T, error::Error>;
pub trait FixedWidth {
fn fields() -> FieldSet;
}
#[derive(Debug, Clone, Copy, PartialEq)]
pub enum Justify {
Left,
Right,
}
impl<T: AsRef<str>> From<T> for Justify {
fn from(s: T) -> Self {
match s.as_ref().to_lowercase().trim() {
"right" => Justify::Right,
"left" => Justify::Left,
_ => panic!("Justify must be 'left' or 'right'"),
}
}
}
#[derive(Debug, Clone)]
pub struct FieldConfig {
name: Option<String>,
range: Range<usize>,
pad_with: char,
justify: Justify,
}
impl Default for FieldConfig {
fn default() -> Self {
Self {
name: None,
range: 0..0,
pad_with: ' ',
justify: Justify::Left,
}
}
}
impl FieldConfig {
pub fn new(range: Range<usize>) -> Self {
FieldConfig {
range,
..Default::default()
}
}
fn width(&self) -> usize {
self.range.end - self.range.start
}
}
#[derive(Debug, Clone)]
pub enum FieldSet {
Item(FieldConfig),
Seq(Vec<FieldSet>),
}
impl FieldSet {
pub fn new_field(range: std::ops::Range<usize>) -> Self {
Self::Item(FieldConfig {
range,
..Default::default()
})
}
pub fn name<T: Into<String>>(mut self, val: T) -> Self {
match &mut self {
Self::Item(conf) => {
conf.name = Some(val.into());
self
}
_ => panic!("Setting name on FieldSet::Seq is not feasible."),
}
}
pub fn pad_with(mut self, val: char) -> Self {
match self {
Self::Item(ref mut config) => {
config.pad_with = val;
self
}
Self::Seq(seq) => Self::Seq(seq.into_iter().map(|fs| fs.pad_with(val)).collect()),
}
}
pub fn justify<T: Into<Justify>>(mut self, val: T) -> Self {
let val = val.into();
match self {
Self::Item(ref mut config) => {
config.justify = val;
self
}
Self::Seq(seq) => Self::Seq(seq.into_iter().map(|fs| fs.justify(val)).collect()),
}
}
pub fn append(self, item: Self) -> Self {
match self {
Self::Item(_) => Self::Seq(vec![self, item]),
Self::Seq(mut seq) => {
seq.append(&mut vec![item]);
Self::Seq(seq)
}
}
}
pub fn extend(self, item: Self) -> Self {
match self {
Self::Item(_) => match item {
Self::Item(_) => self.append(item),
Self::Seq(_) => Self::Seq(vec![self]).extend(item),
},
Self::Seq(mut seq) => {
seq.extend(item);
Self::Seq(seq)
}
}
}
pub fn flatten(self) -> Vec<FieldConfig> {
let mut flatten = vec![];
let mut stack = vec![vec![self]];
while !stack.is_empty() {
let last = stack.last_mut().unwrap();
if last.is_empty() {
stack.pop();
} else {
let field = last.drain(..1).next().unwrap();
match field {
FieldSet::Item(conf) => flatten.push(conf),
FieldSet::Seq(seq) => stack.push(seq.to_vec()),
}
}
}
flatten
}
}
impl IntoIterator for FieldSet {
type Item = FieldSet;
type IntoIter = std::vec::IntoIter<FieldSet>;
fn into_iter(self) -> Self::IntoIter {
match self {
field @ FieldSet::Item(_) => vec![field].into_iter(),
FieldSet::Seq(seq) => seq.into_iter(),
}
}
}
#[derive(Debug, Clone, PartialEq)]
pub enum LineBreak {
None,
Newline,
CRLF,
}
impl LineBreak {
pub fn byte_width(&self) -> usize {
match self {
LineBreak::None => 0,
LineBreak::Newline => 1,
LineBreak::CRLF => 2,
}
}
}
#[cfg(test)]
mod test {
use super::*;
#[test]
fn line_break_byte_width() {
assert_eq!(LineBreak::None.byte_width(), 0);
assert_eq!(LineBreak::Newline.byte_width(), 1);
assert_eq!(LineBreak::CRLF.byte_width(), 2);
}
#[test]
fn fieldset_name() {
let field = FieldSet::new_field(0..0).name("foo");
let field = field.flatten().pop().unwrap();
assert_eq!(field.name.as_ref().unwrap(), "foo");
}
#[test]
#[should_panic]
fn failed_on_fieldset_name() {
FieldSet::Seq(vec![]).name("foo");
}
#[test]
fn fieldset_pad_with() {
let fields = FieldSet::Seq(vec![
FieldSet::new_field(0..1),
FieldSet::Seq(vec![FieldSet::new_field(0..2), FieldSet::new_field(0..3)]),
])
.pad_with('a');
for field in fields.flatten() {
assert_eq!(field.pad_with, 'a')
}
}
#[test]
fn fieldset_justify() {
let fields = FieldSet::Seq(vec![
FieldSet::new_field(0..1),
FieldSet::Seq(vec![FieldSet::new_field(0..2), FieldSet::new_field(0..3)]),
])
.justify(Justify::Right);
for field in fields.flatten() {
assert_eq!(field.justify, Justify::Right)
}
}
#[test]
fn fieldset_justify_str() {
let fields = FieldSet::Seq(vec![
FieldSet::new_field(0..1),
FieldSet::Seq(vec![FieldSet::new_field(0..2), FieldSet::new_field(0..3)]),
])
.justify("right");
for field in fields.flatten() {
assert_eq!(field.justify, Justify::Right)
}
}
#[test]
#[should_panic]
fn fieldset_justify_panic() {
let _ = FieldSet::Seq(vec![
FieldSet::new_field(0..1),
FieldSet::Seq(vec![FieldSet::new_field(0..2), FieldSet::new_field(0..3)]),
])
.justify("foo");
}
#[test]
fn field_building() {
let field = FieldSet::new_field(0..10)
.name("foo")
.pad_with('a')
.justify(Justify::Right);
let field = field.flatten().pop().unwrap();
assert_eq!(field.range, 0..10);
assert_eq!(field.name.as_ref().unwrap(), "foo");
assert_eq!(field.pad_with, 'a');
assert_eq!(field.justify, Justify::Right);
}
}