///|
priv suberror CliError {
CliError(String)
}
///|
fn usage() -> String {
let message =
#|Usage: sleep NUMBER[SUFFIX]...
#|
#|Pause for NUMBER seconds, like sleep(1). NUMBER may be fractional.
#|SUFFIX may be 's' (seconds, default), 'm' (minutes), 'h' (hours), or
#|'d' (days). With multiple arguments, sleep for their sum.
message
}
///|
/// Parse one operand into seconds: a nonnegative (possibly fractional)
/// number with an optional s/m/h/d suffix.
fn parse_duration(text : String) -> Double raise CliError {
let (number, multiplier) = match text {
[.. rest, 's'] => (rest, 1.0)
[.. rest, 'm'] => (rest, 60.0)
[.. rest, 'h'] => (rest, 3600.0)
[.. rest, 'd'] => (rest, 86400.0)
_ => (text, 1.0)
}
// Reject a leading minus up front (so "-1e-9999" cannot underflow to an
// accepted -0.0) and MoonBit digit separators like "1_0", which the
// conventional sleep grammar does not allow.
if number is ['-', ..] {
raise CliError("sleep: invalid time interval: '\{text}'")
}
for c in number {
if c == '_' {
raise CliError("sleep: invalid time interval: '\{text}'")
}
}
let value = @string.parse_double(number) catch {
_ => raise CliError("sleep: invalid time interval: '\{text}'")
}
if value < 0.0 || value != value {
raise CliError("sleep: invalid time interval: '\{text}'")
}
value * multiplier
}
///|
async fn main {
let args = @env.args()[1:]
if args.length() > 0 && (args[0] == "-h" || args[0] == "--help") {
@stdio.stdout.write(usage() + "\n")
return
}
if args.is_empty() {
@stdio.stderr.write("sleep: missing operand\n\n" + usage() + "\n")
@sys.exit(1)
return
}
let mut seconds = 0.0
for arg in args {
seconds += parse_duration(arg) catch {
CliError(msg) => {
@stdio.stderr.write("\{msg}\n")
@sys.exit(1)
return
}
}
}
// @async.sleep takes milliseconds in an Int; round up so sleep never
// waits less than requested, and clamp absurd durations.
let ms = (seconds * 1000.0).ceil()
let clamped = if ms > 2147483647.0 { 2147483647 } else { ms.to_int() }
if clamped > 0 {
@async.sleep(clamped)
}
}