Skip to content

Commit

Permalink
Add parsing to finished Changelog object
Browse files Browse the repository at this point in the history
  • Loading branch information
Sharparam committed Aug 14, 2023
1 parent 1e44805 commit 25fe0c0
Show file tree
Hide file tree
Showing 6 changed files with 189 additions and 7 deletions.
146 changes: 145 additions & 1 deletion crates/lib/src/changelog.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,149 @@
use std::{
collections::{HashMap, HashSet},
str::FromStr,
};

use pest::Parser;
use pest_derive::Parser;

use crate::version::Version;

#[derive(Debug)]
pub struct Changelog {
pub sections: Vec<Section>,
}

#[derive(Debug, Eq, PartialEq)]
pub struct Section {
pub version: Version,
pub date: Option<String>,
pub categories: HashMap<CategoryType, HashSet<String>>,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
pub enum CategoryType {
MajorFeatures,
Features,
MinorFeatures,
Graphics,
Sounds,
Optimizations,
Balancing,
CombatBalancing,
CircuitNetwork,
Changes,
Bugfixes,
Modding,
Scripting,
Gui,
Control,
Translation,
Debug,
EaseOfUse,
Info,
Locale,
Other(String),
}

#[derive(Parser)]
#[grammar = "changelog/grammar.pest"]
pub struct ChangelogParser;
struct ChangelogParser;

#[derive(Debug)]
pub enum ChangelogParseError {
Pest(pest::error::Error<Rule>),
}

pub fn parse(content: &str) -> Result<Changelog, ChangelogParseError> {

Check failure on line 57 in crates/lib/src/changelog.rs

View workflow job for this annotation

GitHub Actions / clippy

the `Err`-variant returned from this function is very large

error: the `Err`-variant returned from this function is very large --> crates/lib/src/changelog.rs:57:32 | 54 | Pest(pest::error::Error<Rule>), | ------------------------------ the largest variant contains at least 184 bytes ... 57 | pub fn parse(content: &str) -> Result<Changelog, ChangelogParseError> { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ | = help: try reducing the size of `changelog::ChangelogParseError`, for example by boxing large elements or replacing it with `Box<changelog::ChangelogParseError>` = help: for further information visit https://rust-lang.github.io/rust-clippy/master/index.html#result_large_err = note: `-D clippy::result-large-err` implied by `-D warnings`
let mut result = Changelog { sections: vec![] };

let changelog = ChangelogParser::parse(Rule::changelog, content)
.map_err(ChangelogParseError::Pest)?
.next()
.unwrap();

for section_pair in changelog.into_inner() {
match section_pair.as_rule() {
Rule::section => {
let mut inner_rules = section_pair.into_inner();
let ver_str = inner_rules.next().unwrap().as_str();
let version = Version::from_str(ver_str).unwrap();

let mut section = Section {
version,
date: None,
categories: HashMap::new(),
};

for remaining in inner_rules {
match remaining.as_rule() {
Rule::date => {
section.date = Some(remaining.as_str().to_owned());
}
Rule::category => {
let mut inner_rules = remaining.into_inner();
let category_type =
CategoryType::from_str(inner_rules.next().unwrap().as_str())
.unwrap();
let entries = section.categories.entry(category_type).or_default();

for entry_pair in inner_rules {
match entry_pair.as_rule() {
Rule::entry => {
let str = entry_pair
.into_inner()
.map(|e| e.as_str())
.collect::<Vec<_>>()
.join("\n");

entries.insert(str);
}
_ => unreachable!(),
}
}
}
_ => unreachable!(),
}
}

result.sections.push(section);
}
Rule::EOI => (),
_ => unreachable!(),
}
}

result.sections.sort_by(|a, b| b.version.cmp(&a.version));

Ok(result)
}

impl FromStr for CategoryType {
type Err = String;

fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"Major Features" => Ok(Self::MajorFeatures),
"Features" => Ok(Self::Features),
"Minor Features" => Ok(Self::MinorFeatures),
"Graphics" => Ok(Self::Graphics),
"Sounds" => Ok(Self::Sounds),
"Optimizations" => Ok(Self::Optimizations),
"Balancing" => Ok(Self::Balancing),
"Combat Balancing" => Ok(Self::CombatBalancing),
"Circuit Network" => Ok(Self::CircuitNetwork),
"Changes" => Ok(Self::Changes),
"Bugfixes" => Ok(Self::Bugfixes),
"Modding" => Ok(Self::Modding),
"Scripting" => Ok(Self::Scripting),
"Gui" => Ok(Self::Gui),
"Control" => Ok(Self::Control),
"Translation" => Ok(Self::Translation),
"Debug" => Ok(Self::Debug),
"Ease of use" => Ok(Self::EaseOfUse),
"Info" => Ok(Self::Info),
"Locale" => Ok(Self::Locale),
o => Ok(Self::Other(o.to_owned())),
}
}
}
2 changes: 1 addition & 1 deletion crates/lib/src/changelog/grammar.pest
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ entry_additional = _{ entry_sub_prefix ~ entry_line }
entry = ${ entry_prefix ~ entry_line ~ (NEWLINE+ ~ entry_additional)* }

category = ${ heading_line ~ (NEWLINE+ ~ entry)+ }
categories = ${ category ~ (NEWLINE+ ~ category)* }
categories = _{ category ~ (NEWLINE+ ~ category)* }

section = ${
section_start ~ NEWLINE ~ version_line ~
Expand Down
35 changes: 30 additions & 5 deletions crates/lib/tests/parsing.rs
Original file line number Diff line number Diff line change
@@ -1,26 +1,51 @@
use facti_lib::changelog::{ChangelogParser, Rule};
use pest::Parser;
use facti_lib::changelog;

#[test]
fn test_basic_changelog() {
let content = include_str!("parsing/changelog.txt");
let changelog = ChangelogParser::parse(Rule::changelog, content);
let changelog = changelog::parse(content);

assert!(changelog.is_ok());
dbg!(changelog.unwrap());
}

#[test]
fn test_multi_section() {
let content = include_str!("parsing/multi_section.txt");
let changelog = ChangelogParser::parse(Rule::changelog, content);
let changelog = changelog::parse(content);

assert!(changelog.is_ok());
}

#[test]
fn test_colon_in_heading() {
let content = include_str!("parsing/colon_in_heading.txt");
let changelog = ChangelogParser::parse(Rule::changelog, content);
let changelog = changelog::parse(content);

assert!(changelog.is_ok());
}

#[test]
fn test_real_parse() {
let content = include_str!("parsing/small.txt");
let changelog = changelog::parse(content);

assert!(changelog.is_ok());
}

#[test]
fn test_minimal() {
let content = include_str!("parsing/minimal.txt");
let changelog = changelog::parse(content);

assert!(changelog.is_ok());
}

#[test]
fn test_wrong_section_order() {
let content = include_str!("parsing/wrong_section_order.txt");
let changelog = changelog::parse(content);

// TODO: Add asserts to check it's now in correct order
assert!(changelog.is_ok());
}
2 changes: 2 additions & 0 deletions crates/lib/tests/parsing/minimal.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
---------------------------------------------------------------------------------------------------
Version: 1.2.3
5 changes: 5 additions & 0 deletions crates/lib/tests/parsing/small.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---------------------------------------------------------------------------------------------------
Version: 1.2.3
Date: 2023-08-14
Features:
- Made parser.
6 changes: 6 additions & 0 deletions crates/lib/tests/parsing/wrong_section_order.txt
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---------------------------------------------------------------------------------------------------
Version: 1.0.0
Date: 2022-01-01
---------------------------------------------------------------------------------------------------
Version: 2.0.0
Date: 2023-05-05

0 comments on commit 25fe0c0

Please sign in to comment.