# curlyconf

[![Apache-2.0 licensed](https://img.shields.io/badge/license-Apache2.0-blue.svg)](https://www.apache.org/licenses/LICENSE-2.0.txt)
[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](http://opensource.org/licenses/MIT)
[![crates.io](https://meritbadge.herokuapp.com/curlyconf)](https://crates.io/crates/curlyconf)
[![Released API docs](https://docs.rs/curlyconf/badge.svg)](https://docs.rs/curlyconf)

### Curlyconf

Curlyconf is a configuration file reader for the configuration
file format used by, for example, named.conf and dhcpd.conf.

### Example config (file.cfg)

```
person charlie {
	fullname "Charlie Brown";
	address 192.168.1.1;
}
person snoopy {
	fullname "Snoopy";
}
```

### Example code

```rust
use serde::Deserialize;

// The initial section of any config is a rust struct.
#[derive(Debug, Deserialize)]
struct Config {
    person: Vec<Person>,
}

#[derive(Debug, Deserialize)]
struct Person {
    #[serde(rename = "__label__")]
    name: String,
    #[serde(default)]
    fullname: Option<String>,
    #[serde(default)]
    address: Option<std::net::IpAddr>,
}

fn main() {
    // Read the configuration file.
    let config: Config = match curlyconf::from_file("file.cfg") {
        Ok(cfg) => cfg,
        Err(e) => {
            eprintln!("{}", e);
            std::process::exit(1);
        }
    };

    // Print what we got (println!("{:?}", config) would be easier...).
    for (i, p) in config.person.iter().enumerate() {
        println!("{}: {} fullname {:?} addr {:?}", i, p.name, p.fullname, p.address);
    }
}

```

### This will print:

```
0: charlie fullname Some("Charlie Brown") addr Some(V4(192.168.1.1))
1: snoopy fullname Some("Snoopy") addr None
```

Curlyconf uses [serde](https://crates.io/crates/serde) to deserialize the
configuration file values to rust types, just like almost every other
crate that does something similar.

### Sections and values.

The configuration file contains section names, labels, sections, value names, and values:

- **sections**. they have a section\_name, an optional label, and contain
  a list of other sections and values. The rust type of a section is a struct.
- **values**. this is a value\_name, followed by a value. If the value is a `Vec`,
  there can be multiple values, separated by a comma.

A section can only have a label if:

- it is part of a `HashMap<Key, Section>`, or
- it is part of a `Vec<Section>` and the rust struct that corresponds to the
  section has a `__label__` field. That field will be set to the label value.

The label type can be any type, it does not have to be a string - it could
also be, for example, a `PathBuf` or `IpAddr`.

The basic structure of a config file is thus:

```
section_name [label] {
    value_name value [,value...];
    value_name value [,value...];
    section_name [label] {
        value_name value [,value...];
    }
}
```

`Enum`s are also supported (see the `serde` docs) so you can do things like:

```rust
#[derive(Debug, Deserialize)]
struct Config {
    animal: Animal,
}

#[derive(Debug, Deserialize)]
enum Animal {
    Cat {
         purrs: bool,
    },
    Dog {
         barks: bool,
    },
}
```

And then have a config like

```
animal cat {
    purrs;
}
```

### Includes.

In every section it is possible to include another file, or multiple files.
Simply do:

```
include otherconfig.cfg;
```

If the pathname of the included file is relative, it will be interpreted
as relative to the current file being parsed. It's possible to use
wildcards as well, for example:

```
configs {
    include conf.d/*.cfg;
}
```

There are some limitations, the main one is that you cannot use the "include"
statement in the middle of a map or list of subsections. Because of the
"include" statement there, the parser cannot lookahead through that
statement to see if the list is bding continued.


## License

Licensed under either of

 * Apache License, Version 2.0, ([LICENSE-APACHE](LICENSE-APACHE) or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT](LICENSE-MIT) or http://opensource.org/licenses/MIT)

at your option.

### Contribution

Unless you explicitly state otherwise, any contribution intentionally submitted
for inclusion in the work by you, as defined in the Apache-2.0 license, shall
be dual licensed as above, without any additional terms or conditions.
