#file-format #serialization #io #deserialize #file-io #read-write

iodeser

Crate that allows to read from and write to .io file format

18 releases

new 0.6.0 Feb 23, 2025
0.5.3 Feb 19, 2025
0.5.1 Jul 8, 2024
0.5.0 May 24, 2024
0.1.3 Feb 3, 2024

#438 in Parser implementations

Download history 52/week @ 2024-12-10 91/week @ 2025-02-11

91 downloads per month

MIT license

49KB
931 lines

Contains (Cab file, 14KB) .vs/rust-library/v17/.wsuo

Introduction

Repository stores code for Rust library that allows to read from and write to .io file format.

About

.io file is a text format that contains serialized object. Each object is stored between two vertical bars |X| in one line for primitive object or more, for iterables and structures. It is build to be cross-platform, language-independent and human-readable.

You can read more about .io de/serialization file format goal and mission here.

Functions and plans

The current status of both serialization and deserialization:

  • Primitive types
  • Strings
  • Arrays
  • Vectors
  • Hashmaps
  • Structs (Named{} and Tuple() and Unit-like)
  • Generic objects ()
  • Tuples
  • &str type
  • Slices
  • Option
  • Result
  • Combinations of all above
  • Enums (Unit, Unnamed(), Named{})
  • Reference types (&T)
  • Unit type ()

Full list of supported types can be found in this crate's documentation.

Capabilities

  • Serialization of supported types using macro to_io!() using objects reference,
  • Deserialization of supported types using macro from_io!() using .io formatted String and objects type,
  • Renaming structs fields in and from .io formatted String using #[io_name()] helper macro using String literal as argument.
  • Ordering structs fields in and from .io formatted String using #[io_order()] helper macro using either FIRST and LAST keywords or an i16 Integer.
  • Ignoring public fields in de/serialization using #[io_ignore] helper macro
  • Allowing to de/serialize private fields using #[io_allow] helper macro.

Refer to example to see, how these capabilities can be utilized and how they affect the serialized string.

How to use

First, you need to import iodeser crate. Inside of Cargo.toml add:

[dependencies]
# ...
iodeser = "0.6.0"

Next, you need to import crate in a code:

use iodeser::*;

To serialize or deserialize ready objects use macros. Remember about passing desired objects type to deserialization macro.

// serialize
let number: i32 = 37;
let serialized_number = to_io!(number);

//deserialize
let deserialized_number = from_io!(serialized_number, i32).unwrap();

assert_eq(number, deserialized_number);

To de/serialize created structs or enums you need to use derive trait IoDeSer:

#[derive(IoDeSer)]
struct Animal{
    pub name: String
}

let animal = Animal(name: "Cat".into());
let serialized_animal = to_io!(animal);
let deserialized_animal = from_io!(serialized_animal, Animal);

Example usage

use iodeser::*; // required import

#[derive(IoDeSer, Debug, PartialEq)] // required macro derive IoDeSer, Debug and PartialEq is not required
struct Person<'a> {
    #[io_name("Name")]      // optional renaming
    pub name: &'a str,
    #[io_name("SecondName")]  // optional renaming
    pub second_name: Option<&'a str>,
    #[io_name("LastName")]  // optional renaming
    pub last_name: &'a str,
    #[io_name("Age")]       // optional renaming
    #[io_order(LAST)]       // optional ordering using FIRST or LAST keyword
    pub age: u8,
    #[io_name("Address")]   // optional renaming
    #[io_order(FIRST)]      // optional ordering using FIRST or LAST keyword
    pub address: Vec<Address<'a>>,
}

#[derive(IoDeSer, Debug, PartialEq)] // required macro derive, Debug and PartialEq is not required
struct Address<'a> {
    #[io_order(3)]          // optional ordering using integer
    pub city: &'a str,
    #[io_order(1)]          // optional ordering using integer
    pub number: AddressNumberType<'a>,
    #[io_order(2)]          // optional ordering using integer
    pub street: &'a str,
}

#[derive(IoDeSer, Debug, PartialEq)] // required macro derive, Debug and PartialEq is not required
enum AddressNumberType<'a>{
    Numeric(u16),
    String(&'a str)
}

fn main() {
    let person = Person {
        name: "John",
        second_name: None,
        last_name: "Kowalski",
        age: 21,
        address: vec![
            Address {
                city: "Warsaw",
                number: AddressNumberType::Numeric(65),
                street: "Tęczowa",
            },
            Address {
                city: "Hamburg",
                number: AddressNumberType::String("220a"),
                street: "Strasse",
            },
        ],
    };

    let io_serialization: String = to_io!(&person); // serialization
    println!("{}", &io_serialization);

    let person_deserialization: Person = from_io!(io_serialization, Person).unwrap(); // deserialization
    println!("{:?}", &person_deserialization);

    assert_eq!(person, person_deserialization);
}
/*
Output:
|
	Address->|
		|
			number->|
				Numeric->|
					|65|
				|
			|
			street->|Tęczowa|
			city->|Warsaw|
		|
		+
		|
			number->|
				String->|
					|220a|
				|
			|
			street->|Strasse|
			city->|Hamburg|
		|
	|
	Name->|John|
	SecondName->|||
	LastName->|Kowalski|
	Age->|21|
|
Person { name: "John", second_name: None, last_name: "Kowalski", age: 21, address: [Address { city: "Warsaw", number: Numeric(65), street: "Tęczowa" }, Address { city: "Hamburg", number: String("220a"), street: "Strasse" }] }
*/

See more examples on GitHub in examples.

Features

Optional features for now only include crate chrono.

To use it you need to turn on feature in Cargo.toml:

[dependencies]
# ...
iodeser = {version = "0.6.0", features = ["chrono"]}

You can either inclide chrono as a dependency in Cargo.toml or use it from iodeser package:

use iodeser::chrono::*;

Dependencies

~0.2–1MB
~20K SLoC