Parsing
The core library provides robust parsing for SemVer 2.0.0 strings via the version.Version.Read[A] type class.
Basic Usage
import version.*
// Extension methods (via default given Read[String] in instances.scala)
"1.2.3".toVersion // Right(Version(1, 2, 3))
"1.2.3-alpha.1".toVersion // Right(Version(1, 2, 3, alpha.1))
"invalid".toVersion // Left(ParseError(...))
// Unsafe variant throws on invalid input
"1.2.3".toVersionUnsafe // Version(1, 2, 3)
"invalid".toVersionUnsafe // throws ParseError
// Factory methods (equivalent to extension methods)
Version.from("1.2.3") // Right(Version(1, 2, 3))
Version.fromUnsafe("1.2.3") // Version(1, 2, 3)
Explicit Reader Instance
For explicit instance passing or when avoiding implicit resolution:
import version.*
// Use the singleton instance directly
val reader: Version.Read[String] = Version.Read.ReadString
// Use directly
reader.toVersion("1.2.3") // Right(Version(...))
reader.toVersionUnsafe("1.2.3") // Version(...)
Accepted Formats
| Format | Example | Notes |
|---|---|---|
| Core | 1.2.3 |
Required |
| With prefix | v1.2.3, V1.2.3 |
Optional v stripped |
| Pre-release | 1.2.3-alpha.1 |
After - |
| Metadata | 1.2.3+build.456 |
After + |
| Full | 1.2.3-alpha.1+build.456 |
Pre-release before metadata |
Pre-release Formats
The default resolver recognises these classifiers:
| Input | Parsed As |
|---|---|
1.0.0-alpha.1 |
Alpha, number 1 |
1.0.0-a.1 |
Alpha, number 1 |
1.0.0-beta.2 |
Beta, number 2 |
1.0.0-b.2 |
Beta, number 2 |
1.0.0-rc.3 |
Release Candidate, number 3 |
1.0.0-cr.3 |
Release Candidate, number 3 |
1.0.0-m.1 |
Milestone, number 1 |
1.0.0-dev.1 |
Dev, number 1 |
1.0.0-SNAPSHOT |
Snapshot (canonical) |
1.0.0-snapshot |
Snapshot (case-insensitive) |
Common variations are normalised:
| Input | Normalised |
|---|---|
1.0.0-RC1 |
rc.1 |
1.0.0-alpha1 |
alpha.1 |
Build Metadata
Metadata identifiers must match [0-9A-Za-z-]+:
"1.2.3+sha.abc123".toVersion
// Right(Version(1, 2, 3, None, Some(Metadata(sha, abc123))))
"1.2.3+build.456.dirty".toVersion
// Right(..., Some(Metadata(build, 456, dirty)))
Custom Pre-release Mapping
Implement version.PreRelease.Resolver to handle non-standard formats:
import version.*
given PreRelease.Resolver with
extension (identifiers: List[String])
def resolve: Option[PreRelease] =
identifiers match
// Treat "nightly" as snapshot
case List("nightly") =>
Some(PreRelease.snapshot)
// Treat "preview.N" as alpha
case List("preview", n) =>
n.toIntOption
.flatMap(i => PreReleaseNumber.from(i).toOption)
.map(PreRelease.alpha)
// Delegate to default resolver
case _ =>
PreRelease.Resolver.given_Resolver.resolve(identifiers)
"1.0.0-nightly".toVersion // Right(Version(1, 0, 0, snapshot))
"1.0.0-preview.3".toVersion // Right(Version(1, 0, 0, alpha.3))
Custom Read Instances
Implement version.Version.Read[A] to parse custom input types. The typeclass requires a contextual version.PreRelease.Resolver for pre-release identifier mapping:
import version.*
import version.errors.InvalidVersionFormat
case class MyVersionFormat(major: Int, minor: Int, patch: Int)
given Version.Read[MyVersionFormat] with
extension (m: MyVersionFormat)
def toVersion(using PreRelease.Resolver): Either[errors.ParseError, Version] =
// Component from() methods return Either[InvalidComponent, T]
// We map errors to ParseError for consistency with the Read contract
for
major <- MajorVersion.from(m.major).left.map(_ =>
InvalidVersionFormat(s"${m.major}.${m.minor}.${m.patch}"))
minor <- MinorVersion.from(m.minor).left.map(_ =>
InvalidVersionFormat(s"${m.major}.${m.minor}.${m.patch}"))
patch <- PatchNumber.from(m.patch).left.map(_ =>
InvalidVersionFormat(s"${m.major}.${m.minor}.${m.patch}"))
yield Version(major, minor, patch)
def toVersionUnsafe(using PreRelease.Resolver): Version =
toVersion match
case Right(v) => v
case Left(e) => throw e
// Now works with factory methods
Version.from(MyVersionFormat(1, 2, 3)) // Right(Version(1, 2, 3))
Error Handling
Parse errors provide context:
import version.errors.*
"abc".toVersion match
case Left(err: ParseError) =>
err.input // "abc"
err.message // Descriptive error
case Right(v) =>
// ...
Validation Summary
| Component | Constraint | Error |
|---|---|---|
| Major | >= 0 | InvalidMajorVersion |
| Minor | >= 0 | InvalidMinorVersion |
| Patch | >= 0 | InvalidPatchNumber |
| Pre-release number | >= 1 | InvalidPreReleaseNumber |
| Build metadata | Non-empty, valid chars | InvalidMetadata |
In this article