Released my first Rust libraries: toml-config and indexed-line-reader

My first experience with Rust has been very positive, I've started working on a side project that might see the light in the next few months and while building it I've ended up creating a couple of libraries that might be useful: toml-config and indexed-line-reader

toml-config

The first one is a library that can be used to load TOML files into a target Rust structure.

To load a file into a Config struct, use ConfigFactory.

Either rustc_serialize or serde can be used for serialization.

Example using rustc_serialize

extern crate rustc_serialize;
extern crate toml_config;

use rustc_serialize::{Encodable, Decodable};
use std::path::Path;
use toml_config::ConfigFactory;

#[derive(RustcEncodable, RustcDecodable)]
struct Config  {
    nested: NestedConfig
}

// Defaults will be used for missing/invalid configurations in the TOML config file
impl Default for Config {
    fn default() -> Config {
        Config {
            nested: NestedConfig::default()
        }
    }
}

#[derive(RustcEncodable, RustcDecodable)]
struct NestedConfig  {
    value: String,
    values: Vec<u16>
}

impl Default for NestedConfig {
    fn default() -> NestedConfig {
        NestedConfig {
            value: "default".to_owned(),
            values: vec![0, 0, 0]
        }
    }
}

/* config.toml:
 * [nested]
 * value = "test"
 * values = [1, 2, 3]
 */

let config: Config = ConfigFactory::load(Path::new("config.toml"));
assert_eq!(config.nested.value, "test");
assert_eq!(config.nested.values, vec![1, 2, 3]);

Example using serde

extern crate serde;
extern crate toml_config;

use serde::{Serialize, Deserialize};
use std::path::Path;
use toml_config::ConfigFactory;

#[derive(Serialize, Deserialize)]
struct Config  {
    nested: NestedConfig
}

// Defaults will be used for missing/invalid configurations in the TOML config file
impl Default for Config {
    fn default() -> Config {
        Config {
            nested: NestedConfig::default()
        }
    }
}

#[derive(Serialize, Deserialize)]
struct NestedConfig  {
    value: String,
    values: Vec<u16>
}

impl Default for NestedConfig {
    fn default() -> NestedConfig {
        NestedConfig {
            value: "default".to_owned(),
            values: vec![0, 0, 0]
        }
    }
}

/* config.toml:
 * [nested]
 * value = "test"
 * values = [1, 2, 3]
 */

let config: Config = ConfigFactory::load(Path::new("config.toml"));
assert_eq!(config.nested.value, "test");
assert_eq!(config.nested.values, vec![1, 2, 3]);

indexed-line-reader

The second is a wrapper on top of the BufReader implementation that internally indexes the byte count every n lines.

IndexedLineReader implements the Seek trait to allow seeking to specific lines.

Index Granularity

IndexedLineReader just stores a byte count every n lines to allow fast seeking by line. The granularity can be configured by passing the valut to the IndexedLineReader constructor.

There is a tradeoff to make between memory occupied by the index and the seek speed, in general lower granularity means more line indexed and higher memory consumption, but fast seek time, while a higher granularity slows down the seek time but less indexes are kept in memory.

As an example, if a file has 100,000,000 lines, usually a granularity of 100,000 gives a good tradeoff between performance and memory consumption.

Example

extern crate indexed_line_reader;

use indexed_line_reader::*;
use std::fs::*;
use std::io::{BufRead, BufReader, Seek, SeekFrom, Write};

/* Creates an IndexedLineReader for a file with index granularity of 100 */
let file_reader = OpenOptions::new().read(true).open("file.txt").expect("Unable to open file reader");
let mut indexed_line_reader = &mut IndexedLineReader::new(BufReader::new(file_reader), 100);

/* Seeks to line 100 from the start of the file */
indexed_line_reader.seek(SeekFrom::Start(100));

/* Seeks forward by 50 lines from the current position on the file */
indexed_line_reader.seek(SeekFrom::Current(50));

/* Seeks backward by 50 lines from the current position on the file */
indexed_line_reader.seek(SeekFrom::Current(-50));

/* Seeks to the 100th line from the end on the file */
indexed_line_reader.seek(SeekFrom::End(100));

If you like them make sure to share.