10 releases

0.4.3 Oct 2, 2024
0.4.2 Aug 22, 2024
0.4.1 Jan 27, 2023
0.4.1-rc.0 Dec 18, 2023
0.1.1 May 24, 2022

#162 in Procedural macros

Download history 234/week @ 2024-08-17 133/week @ 2024-08-24 143/week @ 2024-08-31 138/week @ 2024-09-07 119/week @ 2024-09-14 147/week @ 2024-09-21 491/week @ 2024-09-28 87/week @ 2024-10-05 204/week @ 2024-10-12 98/week @ 2024-10-19 161/week @ 2024-10-26 106/week @ 2024-11-02 144/week @ 2024-11-09 93/week @ 2024-11-16 165/week @ 2024-11-23 185/week @ 2024-11-30

611 downloads per month

MIT license

16KB
204 lines

positional.rs Build Status

This is a library for authoring and parsing positional files

RustDoc


lib.rs:

This is a library to parse and write positional files

Getting Started

You start by defining your own struct that represent a single row in the positional file

struct RowData {
    name: String,
    surname: String,
    age: i32,
}

If you have the data in memory and want to serialize the struct in a positional file you need to annotate the struct with the ToPositionalRow derive.

use positional::ToPositionalRow;

#[derive(ToPositionalRow)]
struct RowData {
    #[field(size = 10)]
    name: String,
    #[field(size = 10, filler = '-')]
    surname: String,
    #[field(size = 5, align = "right")]
    age: i32,
}
let row_data = RowData {
    name: "test".to_string(),
    surname: "test".to_string(),
    age: 20,
};
assert_eq!("test      test------   20", row_data.to_positional_row());

If you are parsing a file you can use the FromPositionalRow derive

use positional::FromPositionalRow;

#[derive(FromPositionalRow, PartialEq, Debug)]
struct RowData {
    #[field(size = 10)]
    name: String,
    #[field(size = 10, filler = '-')]
    surname: String,
    #[field(size = 5, align = "right")]
    age: i32,
}

let row_data = RowData {
    name: "test".to_string(),
    surname: "test".to_string(),
    age: 20,
};

assert_eq!(RowData::from_positional_row("test      test------   20").unwrap(), row_data);

You can use both on the same struct if that makes sense in your domain model.

We annotate the struct to be serializable to/deserializable from a positional row. We also need to annotate every field in the struct to configure the field specification.

Possible attributes are:

attribute name mandatory type default description
size yes number --- define the size of the field in the positional row
filler no char whitespace define what represent the empty space in the field
align no string "left" define the alignment of the field. It could be left or right

Use your own types

Fields are not limited to simple types like String or i32, you can use any type as long as it implements the trait FromStr for parsing and ToString for serializing. For the ToString implementation the library will take care of fill and trim the values for the positional representation. You just need to take care of converting the value to a string.

Files with multiple row types

It could happen that positional files contains more than one line type. In those cases normally you can tell one row from the other by looking at one particular position in the row that identifies the row type. This is useful only for parsing, serializing is basically the same. You can use an enum to represent all rows inside a file.

use positional::{FromPositionalRow, ToPositionalRow};

#[derive(FromPositionalRow, ToPositionalRow)]
enum Rows {
    #[matcher(&row[0..2] == "10")]
    Row1(Row1Data),
    #[matcher(&row[0..2] == "20")]
    Row2(Row2Data),
}

#[derive(FromPositionalRow, ToPositionalRow)]
struct Row1Data {
    #[field(size = 2)]
    row_type: String,
    #[field(size = 20)]
    name: String,
    #[field(size = 20, align = "right")]
    age: i32,
}

#[derive(FromPositionalRow, ToPositionalRow)]
struct Row2Data {
    #[field(size = 2)]
    row_type: String,
    #[field(size = 20)]
    name: String,
    #[field(size = 20, align = "right")]
    age: i32,
}

The enum should have variants with one (and only one) anonymous parameter. To tell the row you annotate the enum variants with matcher attribute and provide an expression. The expression can access the row variable, which contains the full row as a string. In the example we are matching the two starting chars from the string with a given value

How it works

Under the hood, the library just deals with 2 traits: [FromPositionalRow], and [ToPositionalRow] You could use those traits and just use the positional library to handle the actual parsing/creation of the positional files.

The procedural macros FromPositionalRow and ToPositionalRow just do the implementation for you, by leveraging on annotations and rust type system.

Dependencies

~3–10MB
~106K SLoC