///|
/// Prints concise command usage.
fn print_usage() -> Unit {
println("MoonRRule - timezone-safe scheduling toolkit")
println("")
println("Usage:")
println(" moonrrule rrule [COUNT]")
println(" moonrrule cron [COUNT]")
println(" moonrrule zone ")
println(" moonrrule validate-rrule ")
println(" moonrrule validate-cron ")
println("")
println("Examples:")
println(" moonrrule rrule \"FREQ=DAILY;COUNT=3\" 2026-08-01T09:00:00Z")
println(" moonrrule cron \"0 9 * * MON-FRI\" 2026-08-01T00:00:00Z 5")
println(" moonrrule zone Asia/Shanghai 2026-08-01T01:00:00Z")
}
///|
/// Parses an optional positive output count.
fn output_count(args : Array[String], index : Int) -> Int {
if args.length() <= index {
return 10
}
let value = @string.parse_int(args[index]) catch { _ => return 10 }
value.max(1).min(1000)
}
///|
/// Executes RRULE expansion.
fn run_rrule(args : Array[String]) -> Unit {
if args.length() < 4 {
println("error: rrule requires RULE and DTSTART")
return
}
let rule = @rrule.parse(args[2]) catch {
@rrule.RuleError(message) => {
println("invalid RRULE: " + message)
return
}
}
let start = @calendar.parse_local_datetime(args[3]) catch {
@calendar.CalendarError(message) => {
println("invalid DTSTART: " + message)
return
}
}
let count = output_count(args, 4)
let values = @rrule.take(rule, start, count) catch {
@rrule.RuleError(message) => {
println("generation failed: " + message)
return
}
}
println("rule=" + rule.to_string())
println("dtstart=" + start.to_iso_string())
for index, value in values {
println((index + 1).to_string() + "\t" + value.to_iso_string())
}
}
///|
/// Executes Cron expansion.
fn run_cron(args : Array[String]) -> Unit {
if args.length() < 4 {
println("error: cron requires EXPRESSION and AFTER")
return
}
let expression = @cron.parse(args[2]) catch {
@cron.CronError(message) => {
println("invalid Cron expression: " + message)
return
}
}
let after = @calendar.parse_local_datetime(args[3]) catch {
@calendar.CalendarError(message) => {
println("invalid AFTER value: " + message)
return
}
}
let count = output_count(args, 4)
let values = expression.next_n(after, count) catch {
@cron.CronError(message) => {
println("Cron search failed: " + message)
return
}
}
println("cron=" + expression.to_string())
for index, value in values {
println((index + 1).to_string() + "\t" + value.to_iso_string())
}
}
///|
/// Converts a UTC instant to one built-in timezone.
fn run_zone(args : Array[String]) -> Unit {
if args.length() < 4 {
println("error: zone requires ZONE and UTC-INSTANT")
return
}
guard @timezone.by_name(args[2]) is Some(zone) else {
println(
"unknown zone; built-ins: UTC, Asia/Shanghai, Europe/Berlin, America/New_York",
)
return
}
let utc_value = @calendar.parse_local_datetime(args[3]) catch {
@calendar.CalendarError(message) => {
println("invalid UTC instant: " + message)
return
}
}
let instant = utc_value.to_utc_instant()
let wall_time = zone.to_local(instant) catch {
@timezone.TimeZoneError(message) => {
println("timezone conversion failed: " + message)
return
}
}
println("zone=" + zone.name())
println("utc=" + utc_value.to_iso_string() + "Z")
println("local=" + wall_time.to_iso_string())
println("offset=" + zone.offset_at(instant).to_string())
}
///|
/// Validates and canonicalizes one RRULE.
fn validate_rrule(args : Array[String]) -> Unit {
if args.length() < 3 {
println("error: validate-rrule requires RULE")
return
}
let rule = @rrule.parse(args[2]) catch {
@rrule.RuleError(message) => {
println("invalid\n" + message)
return
}
}
println("valid\n" + rule.to_string())
}
///|
/// Validates and explains one Cron expression.
fn validate_cron(args : Array[String]) -> Unit {
if args.length() < 3 {
println("error: validate-cron requires EXPRESSION")
return
}
let expression = @cron.parse(args[2]) catch {
@cron.CronError(message) => {
println("invalid\n" + message)
return
}
}
println("valid\n" + expression.explain())
}
///|
/// MoonRRule command entry point.
fn main {
let args = @env.args()
if args.length() < 2 {
print_usage()
return
}
match args[1] {
"rrule" => run_rrule(args)
"cron" => run_cron(args)
"zone" => run_zone(args)
"validate-rrule" => validate_rrule(args)
"validate-cron" => validate_cron(args)
"help" | "--help" | "-h" => print_usage()
command => {
println("error: unknown command '" + command + "'")
print_usage()
}
}
}