///|
let usage_text : String =
#|moonver [args]
#|
#|Commands:
#| init [--release ] [--alpha ] [--beta ] [--rc ] [--force]
#| get
#| up
#|
#|Examples:
#| moonver get alpha 1.2.3
#| moonver up rc 1.2.3-rc.1
#| moonver up release 1.2.3-rc.1
#|
#|Notes:
#| - version may be prefixed with "v" (e.g. v1.2.3)
///|
fn print_usage() -> Unit {
println(usage_text)
}
///|
fn strip_v(version : String) -> String {
match version {
[.. "v", .. rest] => rest.to_owned()
[.. "V", .. rest] => rest.to_owned()
_ => version
}
}
///|
fn parse_version(value : String) -> @semver.SemVer? {
let normalized = strip_v(value)
match @semver.try_parse(normalized) {
Ok(version) => Some(version)
Err(_) => None
}
}
///|
fn release_version(version : @semver.SemVer) -> @semver.SemVer {
@semver.SemVer::new(version.major, version.minor, version.patch)
}
///|
fn keep_prerelease(version : @semver.SemVer) -> @semver.SemVer {
version.strip_build()
}
///|
fn with_prerelease(
version : @semver.SemVer,
label : String,
number : Int,
) -> @semver.SemVer {
@semver.SemVer::with_prerelease(
version.major,
version.minor,
version.patch,
label,
number,
)
}
///|
fn get_prerelease(
version : @semver.SemVer,
label : String,
base : Int,
) -> @semver.SemVer {
match version.pre {
[@semver.PreId::Str(name), @semver.PreId::Num(_), ..] if name == label =>
keep_prerelease(version)
[@semver.PreId::Str(name), ..] if name == label =>
with_prerelease(version, label, base)
_ => with_prerelease(version, label, base)
}
}
///|
fn up_prerelease(version : @semver.SemVer, label : String) -> @semver.SemVer {
match version.pre {
[@semver.PreId::Str(name), @semver.PreId::Num(num), ..] if name == label =>
with_prerelease(version, label, num + 1)
[@semver.PreId::Str(name), ..] if name == label =>
with_prerelease(version, label, 1)
_ => with_prerelease(release_version(version), label, 1)
}
}
///|
fn up_release(version : @semver.SemVer) -> @semver.SemVer {
if version.pre.is_empty() {
@semver.SemVer::new(version.major, version.minor, version.patch + 1)
} else {
release_version(version)
}
}
///|
fn up_minor(version : @semver.SemVer) -> @semver.SemVer {
@semver.SemVer::new(version.major, version.minor + 1, 0)
}
///|
fn up_major(version : @semver.SemVer) -> @semver.SemVer {
@semver.SemVer::new(version.major + 1, 0, 0)
}
///|
fn get_version(kind : String, version : @semver.SemVer) -> @semver.SemVer? {
match kind {
"alpha" => Some(get_prerelease(version, "alpha", 0))
"beta" => Some(get_prerelease(version, "beta", 0))
"rc" => Some(get_prerelease(version, "rc", 0))
"release" => Some(release_version(version))
_ => None
}
}
///|
fn up_version(kind : String, version : @semver.SemVer) -> @semver.SemVer? {
match kind {
"alpha" => Some(up_prerelease(version, "alpha"))
"beta" => Some(up_prerelease(version, "beta"))
"rc" => Some(up_prerelease(version, "rc"))
"release" => Some(up_release(version))
"minor" => Some(up_minor(version))
"major" => Some(up_major(version))
_ => None
}
}
///|
fn to_output(version : @semver.SemVer) -> String {
"v" + version.to_string()
}
///|
fn parse_non_neg_int(value : String) -> Int? {
try {
let num = @string.parse_int(value)
if num >= 0 {
Some(num)
} else {
None
}
} catch {
_ => None
}
}
///|
fn parse_init_options(
args : ArrayView[String],
release : String,
alpha : Int,
beta : Int,
rc : Int,
) -> Result[(String, Int, Int, Int), String] {
match args {
[] => Ok((release, alpha, beta, rc))
["-r", value, .. rest] => parse_init_options(rest, value, alpha, beta, rc)
["--release", value, .. rest] =>
parse_init_options(rest, value, alpha, beta, rc)
["-a", value, .. rest] =>
match parse_non_neg_int(value) {
Some(num) => parse_init_options(rest, release, num, beta, rc)
None => Err("invalid alpha: \{value}")
}
["--alpha", value, .. rest] =>
match parse_non_neg_int(value) {
Some(num) => parse_init_options(rest, release, num, beta, rc)
None => Err("invalid alpha: \{value}")
}
["-b", value, .. rest] =>
match parse_non_neg_int(value) {
Some(num) => parse_init_options(rest, release, alpha, num, rc)
None => Err("invalid beta: \{value}")
}
["--beta", value, .. rest] =>
match parse_non_neg_int(value) {
Some(num) => parse_init_options(rest, release, alpha, num, rc)
None => Err("invalid beta: \{value}")
}
["-c", value, .. rest] =>
match parse_non_neg_int(value) {
Some(num) => parse_init_options(rest, release, alpha, beta, num)
None => Err("invalid rc: \{value}")
}
["--rc", value, .. rest] =>
match parse_non_neg_int(value) {
Some(num) => parse_init_options(rest, release, alpha, beta, num)
None => Err("invalid rc: \{value}")
}
["--force", .. rest] => parse_init_options(rest, release, alpha, beta, rc)
[unknown, ..] => Err("unknown option: \{unknown}")
}
}
///|
fn handle_init(args : ArrayView[String]) -> Unit {
let result = parse_init_options(args, "1.0.0", 0, 0, 0)
match result {
Err(msg) => {
println(msg)
print_usage()
}
Ok((release, alpha, beta, rc)) =>
match parse_version(release) {
None => {
println("invalid release: \{release}")
print_usage()
}
Some(version) => {
let clean = release_version(version)
println("release: \{clean.to_string()}")
println("alpha: \{alpha}")
println("beta: \{beta}")
println("rc: \{rc}")
}
}
}
}
///|
fn handle_get(kind : String, version_raw : String) -> Unit {
match parse_version(version_raw) {
None => {
println("invalid version: \{version_raw}")
print_usage()
}
Some(version) =>
match get_version(kind, version) {
None => {
println("unknown get target: \{kind}")
print_usage()
}
Some(next) => println(to_output(next))
}
}
}
///|
fn handle_up(kind : String, version_raw : String) -> Unit {
match parse_version(version_raw) {
None => {
println("invalid version: \{version_raw}")
print_usage()
}
Some(version) =>
match up_version(kind, version) {
None => {
println("unknown up target: \{kind}")
print_usage()
}
Some(next) => println(to_output(next))
}
}
}
///|
fn main {
let args = @env.args()
match args {
[_, "-h", ..] => print_usage()
[_, "--help", ..] => print_usage()
[_, "help", ..] => print_usage()
[_, "init", .. rest] => handle_init(rest)
[_, "get", kind, version, ..] => handle_get(kind, version)
[_, "up", kind, version, ..] => handle_up(kind, version)
_ => print_usage()
}
}