2 unstable releases

0.2.0 Aug 26, 2024
0.1.0 Jun 26, 2022

#1184 in Parser implementations


Used in 3 crates (2 directly)

MIT/Apache

5KB

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 bool 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 as config files viable.

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, Error> {
    // a Vec<String>
    let mut current_path = Vec::new();
    // probably some map, like be a HashMap<Vec<String>, Value>
    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() {
        if let Ok(line) = line {
            // if current line is a header
            if let Some(success) = parse_header(&current_path, &line) {
                if let Ok(path_changes) = success {
                    // add parsed value to previous subheader
                    result.add(current_path.clone(), value);
                    // start parsing next subsection
                    value = Value::new();
                    // apply path changes
                    path_changes.apply(&mut current_path);
                } else {
                    return Err(Error::SubheaderWithoutHeader);
                }
            }
        } else {
            // parse the content inbetween headers
            value.add_and_parse_line(line)?;
        }
    }

    result.add(value);

    Ok(result)
}

No runtime deps