#header-parser #markdown-parser #header #parser #markdown #config-parser #config-file

header-parsing

Simplifies parsing the headers of markdown inspired file formats

4 releases (2 breaking)

new 0.3.1 Mar 3, 2025
0.3.0 Feb 25, 2025
0.2.0 Aug 26, 2024
0.1.0 Jun 26, 2022

#1124 in Parser implementations

Download history 3/week @ 2024-11-16 1/week @ 2024-11-23 25/week @ 2024-12-07 9/week @ 2024-12-14 1/week @ 2024-12-21 3/week @ 2025-02-01 27/week @ 2025-02-08 16/week @ 2025-02-15 114/week @ 2025-02-22 155/week @ 2025-03-01

313 downloads per month
Used in 5 crates (4 directly)

MIT/Apache

7KB

Header parsing

This library is meant to help with parsing markdown inspired files, where headers are marked by a sequence of "#".

The only function parse_header is meant to be used when parsing a file line by line. If the line starts with a "#", the function will return Some result to indicate if it's a valid format, else it will return None.

The only invalid format is when a header starts with more than one "#" more than the previous header.

This makes using markdown like formats viable for config files.

Example config

Headers and subheaders are stored as array.

For example you could have a file like this:

A

# Header 1

B

## Subheader 1

C

## Subheader 2

D

# Header 2

E

## Subheader 1

F

## Subheader 2

G

The letters would belong to different headers:

  • A belongs to no subheader ([])
  • B belongs to "Header 1" (["Header 1"])
  • C belongs to "Subheader 1" of "Header 1" (["Header 1", "Subheader 1"])
  • D belongs to "Subheader 2" of "Header 1" (["Header 1", "Subheader 2"])
  • E belongs to "Header 2" (["Header 2"])
  • F belongs to "Subheader 1" of "Header 2" (["Header 2", "Subheader 1"])
  • G belongs to "Subheader 2" of "Header 2" (["Header 2", "Subheader 2"])

Usage

You have to store the path of headers and subheader yourself. This way, you are allowed to handle the sections inbetween the headers as you want.

use header_parsing::parse_header;

use std::io::{BufRead, BufReader, Read};

enum Error {
    SubheaderWithoutHeader,
    ... // Custom error types
}

fn parse<R: Read>(reader: R) -> Result<Map<Vec<Box<str>>, Value>, Error> {
    // a `Vec<Box<str>>`
    let mut current_path = Vec::new();
    // the result is likely a map type, like a `HashMap`
    let mut result = Map::new();
    // the content inbetween the headers parsed into the wanted format
    let mut value = Value::new();

    for line in BufReader::new(reader).lines() {
        let Ok(line) = line else {
            return Err(...);
        };

        // if current line is a header
        if let Some(success) = parse_header(&mut current_path, &line) {
            if let Ok(path_changes) = success {
                // add parsed value to previous subheader
                result.insert(path_changes.path.clone(), value);
                // start parsing next subsection
                value = Value::new();
                // apply path changes
                path_changes.apply();
            } else {
                return Err(Error::SubheaderWithoutHeader);
            }
        } else {
            // parse the content inbetween headers
            value.modify(parse_line(&line)?);
        }
    }

    result.insert(current_path, value);

    Ok(result)
}

No runtime deps