Skip to content

Commit

Permalink
refactor(conventional): definition and optional sections model and pa…
Browse files Browse the repository at this point in the history
…rsing
  • Loading branch information
pedraal committed Aug 16, 2024
1 parent 73af227 commit 6e3dd69
Show file tree
Hide file tree
Showing 2 changed files with 139 additions and 88 deletions.
138 changes: 88 additions & 50 deletions src/releam.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,10 @@ pub type CommitType {
Ci
Chore
Revert
Custom(String)
}

pub type ConventionalCommitParseError {
InvalidCommitType
InvalidCommitDefinition
InvalidCommitMessage
}
Expand Down Expand Up @@ -50,7 +50,24 @@ pub type ConventionalAttributes {
message: String,
body: List(String),
footer: List(#(String, String)),
breaking_change: Bool,
breaking: Bool,
)
}

pub type ConventionalDefinition {
ConventionalDefinition(
commit_type: CommitType,
scope: Option(String),
message: String,
breaking: Bool,
)
}

pub type ConventionalOptionalSections {
ConventionalOptionalSections(
body: List(String),
footer: List(#(String, String)),
breaking: Bool,
)
}

Expand Down Expand Up @@ -146,20 +163,31 @@ pub fn parse_conventional_attributes(message: String) {
|> io.debug

case sections {
[def] -> parse_conventional_definition(def)
[def] -> {
parse_conventional_definition(def)
|> result.map(fn(cd) {
ConventionalAttributes(
commit_type: cd.commit_type,
scope: cd.scope,
message: cd.message,
body: [],
footer: [],
breaking: cd.breaking,
)
})
}
[def, ..rest] -> {
parse_conventional_definition(def)
|> result.map(fn(conventional_attributes) {
let #(body, footer) = parse_conventional_optional_sections(rest)
let is_breaking_change =
list.any(footer, fn(item) { item.0 == "BREAKING CHANGE" })
|> result.map(fn(cd) {
let cos = parse_conventional_optional_sections(rest)

ConventionalAttributes(
..conventional_attributes,
body: body,
footer: footer,
breaking_change: conventional_attributes.breaking_change
|| is_breaking_change,
commit_type: cd.commit_type,
scope: cd.scope,
message: cd.message,
body: cos.body,
footer: cos.footer,
breaking: cd.breaking || cos.breaking,
)
})
}
Expand All @@ -168,7 +196,7 @@ pub fn parse_conventional_attributes(message: String) {
}

pub fn parse_conventional_definition(def: String) {
let is_breaking_change = string.contains(def, "!:")
let is_breaking = string.contains(def, "!:")
let attributes =
def
|> string.replace(")", "")
Expand All @@ -179,29 +207,19 @@ pub fn parse_conventional_definition(def: String) {

case attributes {
[commit_type, scope, message] ->
parse_conventional_commit_type(commit_type)
|> result.map(fn(c_t) {
ConventionalAttributes(
commit_type: c_t,
scope: Some(scope),
message: message,
body: [],
footer: [],
breaking_change: is_breaking_change,
)
})
Ok(ConventionalDefinition(
commit_type: parse_conventional_commit_type(commit_type),
scope: Some(scope),
message: message,
breaking: is_breaking,
))
[commit_type, message] ->
parse_conventional_commit_type(commit_type)
|> result.map(fn(c_t) {
ConventionalAttributes(
commit_type: c_t,
scope: None,
message: message,
body: [],
footer: [],
breaking_change: is_breaking_change,
)
})
Ok(ConventionalDefinition(
commit_type: parse_conventional_commit_type(commit_type),
scope: None,
message: message,
breaking: is_breaking,
))
_ -> Error(InvalidCommitDefinition)
}
}
Expand All @@ -212,10 +230,30 @@ pub fn parse_conventional_optional_sections(sections: List(String)) {
|> result.unwrap("")
|> parse_conventional_footer

let is_breaking =
result.map(footer, fn(items) {
case list.key_find(items, "BREAKING CHANGE") {
Ok(_) -> True
Error(_) -> False
}
})
|> result.unwrap(False)

case list.reverse(sections), footer {
[_, ..body], Ok(f) -> #(list.reverse(body), f)
body, Error(_) -> #(list.reverse(body), [])
_, _ -> #([], [])
[_, ..body], Ok(f) ->
ConventionalOptionalSections(
body: list.reverse(body),
footer: f,
breaking: is_breaking,
)
body, Error(_) ->
ConventionalOptionalSections(
body: list.reverse(body),
footer: [],
breaking: is_breaking,
)
_, _ ->
ConventionalOptionalSections(body: [], footer: [], breaking: is_breaking)
}
}

Expand Down Expand Up @@ -258,17 +296,17 @@ pub fn parse_conventional_footer_line(line: String) {

pub fn parse_conventional_commit_type(commit_type: String) {
case commit_type {
"feat" -> Ok(Feat)
"fix" -> Ok(Fix)
"docs" -> Ok(Docs)
"style" -> Ok(Style)
"refactor" | "refacto" -> Ok(Refactor)
"perf" -> Ok(Perf)
"test" | "tests" -> Ok(Test)
"build" -> Ok(Build)
"ci" -> Ok(Ci)
"chore" -> Ok(Chore)
"revert" -> Ok(Revert)
_ -> Error(InvalidCommitType)
"feat" -> Feat
"fix" -> Fix
"docs" -> Docs
"style" -> Style
"refactor" | "refacto" -> Refactor
"perf" -> Perf
"test" | "tests" -> Test
"build" -> Build
"ci" -> Ci
"chore" -> Chore
"revert" -> Revert
custom -> Custom(custom)
}
}
89 changes: 51 additions & 38 deletions test/releam_test.gleam
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,9 @@ import gleam/option.{None, Some}
import gleeunit
import gleeunit/should
import releam.{
Build, Chore, Ci, ConventionalAttributes, Docs, Feat, Fix,
InvalidCommitDefinition, InvalidCommitType, InvalidConventionalFooter, Perf,
Refactor, Revert, Style, Test,
Build, Chore, Ci, ConventionalAttributes, ConventionalDefinition,
ConventionalOptionalSections, Custom, Docs, Feat, Fix, InvalidCommitDefinition,
InvalidConventionalFooter, Perf, Refactor, Revert, Style, Test,
}

pub fn main() {
Expand Down Expand Up @@ -118,54 +118,53 @@ pub fn main() {
pub fn parse_conventional_definition_test() {
releam.parse_conventional_definition("feat: lorem ipsum")
|> should.equal(
Ok(ConventionalAttributes(
Ok(ConventionalDefinition(
commit_type: Feat,
scope: None,
message: "lorem ipsum",
body: [],
footer: [],
breaking_change: False,
breaking: False,
)),
)

releam.parse_conventional_definition("feat(api): lorem ipsum")
|> should.equal(
Ok(ConventionalAttributes(
Ok(ConventionalDefinition(
commit_type: Feat,
scope: Some("api"),
message: "lorem ipsum",
body: [],
footer: [],
breaking_change: False,
breaking: False,
)),
)

releam.parse_conventional_definition("feat(api)!: lorem ipsum")
|> should.equal(
Ok(ConventionalAttributes(
Ok(ConventionalDefinition(
commit_type: Feat,
scope: Some("api"),
message: "lorem ipsum",
body: [],
footer: [],
breaking_change: True,
breaking: True,
)),
)

releam.parse_conventional_definition("feat!: lorem ipsum")
|> should.equal(
Ok(ConventionalAttributes(
Ok(ConventionalDefinition(
commit_type: Feat,
scope: None,
message: "lorem ipsum",
body: [],
footer: [],
breaking_change: True,
breaking: True,
)),
)

releam.parse_conventional_definition("foo: lorem ipsum")
|> should.equal(Error(InvalidCommitType))
|> should.equal(
Ok(ConventionalDefinition(
commit_type: Custom("foo"),
scope: None,
message: "lorem ipsum",
breaking: False,
)),
)

releam.parse_conventional_definition("lorem ipsum")
|> should.equal(Error(InvalidCommitDefinition))
Expand All @@ -175,18 +174,29 @@ pub fn parse_conventional_optional_sections_test() {
[
"foo bar", "lorem ipsum",
"Reviewed-by: Z
Refs: #123",
Refs: #123
BREAKING CHANGE: drop json support",
]
|> releam.parse_conventional_optional_sections
|> should.equal(
#(["foo bar", "lorem ipsum"], [#("Reviewed-by", "Z"), #("Refs", "#123")]),
)
|> should.equal(ConventionalOptionalSections(
body: ["foo bar", "lorem ipsum"],
footer: [
#("Reviewed-by", "Z"),
#("Refs", "#123"),
#("BREAKING CHANGE", "drop json support"),
],
breaking: True,
))
}

pub fn parse_conventional_optional_sections_with_invalid_footer_test() {
["foo bar", "lorem ipsum", "Reviewed by: Z"]
|> releam.parse_conventional_optional_sections
|> should.equal(#(["foo bar", "lorem ipsum", "Reviewed by: Z"], []))
|> should.equal(ConventionalOptionalSections(
body: ["foo bar", "lorem ipsum", "Reviewed by: Z"],
footer: [],
breaking: False,
))
}

pub fn parse_conventional_footer_test() {
Expand All @@ -213,41 +223,44 @@ pub fn parse_conventional_footer_with_invalid_test() {

pub fn parse_conventional_commit_type_test() {
releam.parse_conventional_commit_type("feat")
|> should.equal(Ok(Feat))
|> should.equal(Feat)

releam.parse_conventional_commit_type("fix")
|> should.equal(Ok(Fix))
|> should.equal(Fix)

releam.parse_conventional_commit_type("docs")
|> should.equal(Ok(Docs))
|> should.equal(Docs)

releam.parse_conventional_commit_type("style")
|> should.equal(Ok(Style))
|> should.equal(Style)

releam.parse_conventional_commit_type("refactor")
|> should.equal(Ok(Refactor))
|> should.equal(Refactor)

releam.parse_conventional_commit_type("refacto")
|> should.equal(Ok(Refactor))
|> should.equal(Refactor)

releam.parse_conventional_commit_type("perf")
|> should.equal(Ok(Perf))
|> should.equal(Perf)

releam.parse_conventional_commit_type("test")
|> should.equal(Ok(Test))
|> should.equal(Test)

releam.parse_conventional_commit_type("tests")
|> should.equal(Ok(Test))
|> should.equal(Test)

releam.parse_conventional_commit_type("build")
|> should.equal(Ok(Build))
|> should.equal(Build)

releam.parse_conventional_commit_type("ci")
|> should.equal(Ok(Ci))
|> should.equal(Ci)

releam.parse_conventional_commit_type("chore")
|> should.equal(Ok(Chore))
|> should.equal(Chore)

releam.parse_conventional_commit_type("revert")
|> should.equal(Ok(Revert))
|> should.equal(Revert)

releam.parse_conventional_commit_type("whatever")
|> should.equal(Custom("whatever"))
}

0 comments on commit 6e3dd69

Please sign in to comment.