# ini-parser
This project is a Rust-based utility for parsing `.ini` files, which are commonly used to store configuration settings in a simple key-value pair format. By parsing these files, this tool allows automated processing and extraction of configuration data, useful in various applications, including configuration management and system setup.
## Links
- **Crates.io**: [ini-parser on crates.io](https://crates.io/crates/ini-parser)
- **Documentation**: [ini-parser Documentation](https://docs.rs/ini-parser/latest/ini_parser/index.html)
### Example of an `.ini` File
Here’s an example `.ini` file that can be parsed by this tool:
```ini
# A top-level comment
[General]
app_name = MyApplication
version = 1.2.3
description = "An example INI file for testing"
[Database]
host = localhost
port = 5432
username = admin
password = "p@ssw0rd"
[[Database.Connection]]
user = admin
password = secret
[[Database.Settings]]
timeout = 30
pool_size = 10
[Logging]
level=debug
file_path=/var/log/app.log
[Server]
host=0.0.0.0
port=8080
[Features]
enable_feature_x=true
enable_feature_y=false
```
## Technical Description
### Parsing Process
Using the **Pest** library for Rust, this tool analyzes the structure of `.ini` files, extracting key information such as:
- **Section Name** (e.g., `Database`, `Server`)
- **Key-Value Pairs** (e.g., `host=localhost`, `port=5432`)
The `.ini` files are parsed line-by-line, following a grammar that identifies sections, keys, and values. Pest provides a custom grammar defined in `grammar.pest`, which defines these parsing rules in a concise format.
### Grammar
```pest
ini_file = { SOI ~ (comment | section | NEWLINE)* ~ EOI }
WHITESPACE = _{ " " | "\t" }
NEWLINE = { "\n" }
section = {
"[" ~ section_name ~ "]" ~
NEWLINE* ~
(comment | key_value | nested_section | NEWLINE)*
}
nested_section = {
"[[" ~ section_name ~ "]]" ~
NEWLINE* ~
(comment | key_value | NEWLINE)*
}
key_value = {
key ~ WHITESPACE? ~ "=" ~ WHITESPACE? ~ (quoted_value | value)? ~ NEWLINE?
}
quoted_value = { "\"" ~ (!"\"" ~ (ASCII_ALPHANUMERIC | PUNCTUATION | " "))* ~ "\"" }
Here's the breakdown of your updated grammar:
### My Grammar rule by rule:
- **Overall Structure**
The root rule for parsing an INI file is represented as `ini_file`. It allows for multiple sections, comments, and newlines, and must start and end with `SOI` (Start of Input) and `EOI` (End of Input).
```pest
ini_file = { SOI ~ (comment | section | NEWLINE)* ~ EOI }
```
- `SOI`: Start of Input.
- `EOI`: End of Input.
- The file can contain sections, comments, and newlines.
- **Whitespace**
The `WHITESPACE` rule defines space or tab characters as optional spacing in key-value pairs.
```pest
WHITESPACE = _{ " " | "\t" }
```
- `WHITESPACE` can be either a space or a tab.
- **Section**
A section is defined by a name enclosed in square brackets `[]`, with an optional newline and a combination of comments, key-value pairs, or nested sections.
```pest
section = {
"[" ~ section_name ~ "]" ~ NEWLINE* ~ (comment | key_value | nested_section | NEWLINE)*
}
```
- `section_name`: The name of the section.
- The section can contain comments, key-value pairs, or nested sections.
- The section can be followed by newlines.
- **Nested Section**
A nested section is a section enclosed in double square brackets `[[]]`, with similar content as regular sections, including key-value pairs and comments.
```pest
nested_section = {
"[[" ~ section_name ~ "]]" ~ NEWLINE* ~ (comment | key_value | NEWLINE)*
}
```
- This allows for nested sections within a section.
- **Key-Value Pair**
A key-value pair is composed of a key followed by an optional space around the `=` sign, followed by an optional quoted or unquoted value. The key-value pair ends with an optional newline.
```pest
key_value = {
key ~ WHITESPACE? ~ "=" ~ WHITESPACE? ~ (quoted_value | value)? ~ NEWLINE?
}
```
- `key`: The key in the key-value pair.
- `quoted_value | value`: The value of the key, either quoted or unquoted.
- Newline is optional after the key-value pair.
- **Section Name**
A section name is a sequence of one or more alphanumeric characters, underscores, hyphens, or dots.
```pest
section_name = { (ASCII_ALPHANUMERIC | "_" | "-" | ".")+ }
```
- **Key**
The key consists of one or more alphanumeric characters, underscores, or hyphens, excluding the equal sign `=`.
```pest
key = { (ASCII_ALPHANUMERIC | "_" | "-")+ }
```
- **Value**
A simple value consists of ASCII printable characters (except control characters, backslashes, and newlines), including punctuation and spaces.
```pest
value = { (ASCII_ALPHANUMERIC | PUNCTUATION | " ")+ }
```
- **Quoted Value**
A quoted value is enclosed in double quotes and may contain spaces, punctuation, and other special characters, except the quote itself.
```pest
quoted_value = { "\"" ~ (!"\"" ~ (ASCII_ALPHANUMERIC | PUNCTUATION | " "))* ~ "\"" }
```
- **Comment**
A comment starts with `#` or `;` and continues until the end of the line, optionally followed by a newline.
```pest
comment = { ("#" | ";") ~ (!NEWLINE ~ (ASCII_ALPHANUMERIC | PUNCTUATION | " "))* ~ NEWLINE? }
```
- **Newline**
The `NEWLINE` rule matches a newline character `\n`.
```pest
NEWLINE = { "\n" }
```
### Parsing Outcome and Usage
Once parsed, the extracted data is structured into an `IniFile` object. This object contains a list of `Section` objects, each with a name and key-value pairs. You can access and manipulate the configuration data programmatically.
### Example Usage
After parsing an `.ini` file, you can use the data in your application. Here's how you would parse an INI file and access its data:
```rust
use ini_parser::parser::parse;
fn main() {
let input = std::fs::read_to_string("config.ini")
.expect("Error reading INI file");
let ini_file = parse(&input).expect("Failed to parse INI file");
for (section_name, section) in ini_file.sections {
println!("[{}]", section_name);
for (key, value) in §ion.key_values {
println!("{} = {}", key, value);
}
for (nested_name, nested_section) in §ion.nested_sections {
println!("[[{}]]", nested_name);
for (key, value) in &nested_section.key_values {
println!("{} = {}", key, value);
}
}
println!();
}
}
```
### Features
- **INI File Parsing**: Supports parsing sections and key-value pairs.
- **Error Handling**: Gracefully handles errors during parsing and provides useful error messages.
- **Extensible**: The grammar is defined in a modular way, making it easy to extend the parser for other configurations if needed.
### Installation
To use this library in your project, add it to your `Cargo.toml` file:
```toml
[dependencies]
ini_parser = "0.1"
```
Then, you can include the library in your code:
```rust
use ini_parser::parser::parse;
```
### Running Tests
To run tests for this project, use the following command:
```bash
cargo test
```
---
###### Thanks for your attention!