Semantic version parsing
Parse a version string into a record with optional tag and build metadata.
module semanticVersion
type SemanticVersion = (
major: int
minor: int
patch: int
tag?: string
buildMetadata?: string
)
-- Split a string at the first occurrence of a separator, if there is one
def splitOnce: (string, string) -> (string, #some(string) | #none) = { s, separator ->
match s \indexOf(separator)
| #some(i) -> (s \take(i), #some(s \drop(i + separator.length)))
| #notFound -> (s, #none)
}
def parseNumber: string -> #some(int) | #error(#invalidVersionNumber) = { s ->
Int.parse(s) \Result.mapError { _ -> #invalidVersionNumber }
}
-- "1.2.3-beta+build42" has core "1.2.3-beta" and build metadata "build42";
-- the core has numbers "1.2.3" and tag "beta"
export def parse: string -> #some(SemanticVersion) | #error(#invalidVersionNumber) = { value ->
def (core, buildMetadata) = splitOnce(value, "+")
def (numbers, tag) = splitOnce(core, "-")
match numbers \split(".")
| [major, minor, patch] -> proc {
return SemanticVersion(
major = try parseNumber(major)
minor = try parseNumber(minor)
patch = try parseNumber(patch)
tag = tag
buildMetadata = buildMetadata
)
}
| _ -> #error(#invalidVersionNumber)
}
@entry
def main = {
for ["1.2.3", "1.2.3-beta+build42", "1.2", "x.y.z"] | input ->
match parse(input)
| #some(v) -> println("{input}: major {v.major}, minor {v.minor}, patch {v.patch}")
| #error(#invalidVersionNumber) -> println("{input}: not a version number")
}
This program lives in the repository as examples/semanticVersionParsing.nyx.