Sign in

    moon-lander-core

    Shared domain types of the Moon-Lander framework

    Download zip
    Author
    Version
    0.7.0
    License
    Apache-2.0
    Last updated
    7 minutes ago
    Downloads
    203

    #moonrockz/moon-lander-core

    Shared domain types of the Moon-Lander framework: the application name, variable prefix, absolute path and time limit. The other framework modules (moon-lander-config, moon-lander-mediator, moon-lander-hosting and moon-lander) import this module, so a type here has one set of rules for all of them. The module imports nothing except the MoonBit core library.

    Import the root package with an explicit alias, because the module name has - in it:

    ///|
    import {
    "moonrockz/moon-lander-core" @lander_core,
    }

    #Application name

    An AppName starts with an ASCII letter and holds only ASCII letters, digits, - and _. So it is safe in file and directory names. AppName::parse is the only way to make one. A failure raises AppNameError::InvalidAppName with the text and the broken rule as an AppNameRule case, so a caller can match the rule and does not have to read the message:

    ///|
    test "parse an application name and match a failure by its rule" {
    let name = @lander_core.AppName::parse("my-tool")
    assert_eq(name.to_string(), "my-tool")
    let rule = try {
    ignore(@lander_core.AppName::parse("my tool"))
    None
    } catch {
    InvalidAppName(rule~, ..) => Some(rule)
    }
    assert_true(rule is Some(CharNotAllowed(' ')))
    }

    RuleCaseExample that breaks it
    Not emptyEmpty""
    Starts with an ASCII letterFirstNotAsciiLetter9tool, -tool
    Only ASCII letters, digits, - and _CharNotAllowed(char)my tool, my.tool, café

    #Time limit

    A TimeLimit is a positive amount of time, from 1 millisecond to 2 147 483 647 milliseconds (the largest Int, about 24.8 days). That is the range of the timeouts of moonbitlang/async, so to_millis() gives an Int that a timeout can take, and it cannot fail. Make one from a count with TimeLimit::millis(count) or TimeLimit::seconds(count), or parse its text form with TimeLimit::parse(text):

    ///|
    /// A function that takes a timeout as milliseconds, such as
    /// `@async.with_timeout`.
    fn wait_up_to(millis : Int) -> String {
    "waits up to \{millis} ms"
    }

    ///|
    test "parse a time limit and give it to a timeout" {
    let limit = @lander_core.TimeLimit::parse("2m30s")
    assert_eq(wait_up_to(limit.to_millis()), "waits up to 150000 ms")
    assert_eq(limit.to_string(), "2m30s")
    assert_eq(limit, @lander_core.TimeLimit::seconds(150))
    }

    The text form has one or more parts. Each part is a decimal number, an optional space and a unit, and the parts go from the largest unit to the smallest, each unit at most one time: 9ms, 9 ms, 10 seconds, 1.5s, 2m30s, 1h 15m.

    UnitShortLong
    Millisecondmsmillisecond, milliseconds
    Secondssecond, seconds
    Minutemminute, minutes
    Hourhhour, hours

    A failure raises TimeLimitError::InvalidTimeLimit with the input and the broken rule as a TimeLimitRule case:

    RuleCaseExample that breaks it
    Not emptyEmpty""
    At most one space, only inside or between parts; no other whitespaceUnexpectedSpace 10s, 10 s, 10\ts
    Each part starts with a numberNotANumber-1s, .5s
    Each number has a unitMissingUnit5000
    A known unit, in lower caseUnknownUnit(unit)30d, 10 Seconds, 10µs
    Largest unit firstUnitsOutOfOrder30s 1m
    Each unit one timeRepeatedUnit1m 1m
    Each part a whole number of millisecondsNotWholeMilliseconds0.5ms, 1.0005s
    Greater than zeroNotPositive0s
    Not more than the largest limitAboveLargest597h

    to_string() gives the canonical text, from hours to milliseconds with short units and no spaces, for example 1s500ms. The parse reads it back to an equal limit.

    #Variable prefix and absolute path

    EnvPrefix starts with an upper-case ASCII letter or _, followed by upper-case ASCII letters, digits or _. Its parse error gives the input and an EnvPrefixRule case. Deriving a default prefix from an AppName cannot fail.

    AbsolutePath names a location without claiming that it exists. Both working directories and content roots use it. An empty or relative path raises AbsolutePathError with Empty or NotAbsolute. Windows drive roots are accepted. UNC roots need a server and share; an incomplete root raises MissingUncServerOrShare. Windows separators become / only in drive and backslash UNC forms. In a path that starts with a single /, backslashes stay name characters. The text is kept as written, including ., .. and trailing separators; equality compares the stored text.

    ///|
    test "derive a prefix and parse an absolute working directory" {
    let name = @lander_core.AppName::parse("my-tool")
    assert_eq(@lander_core.EnvPrefix::from_app_name(name).to_string(), "MY_TOOL")
    assert_eq(@lander_core.EnvPrefix::parse("MT").to_string(), "MT")
    let directory = @lander_core.AbsolutePath::parse("/work/repo")
    assert_eq(directory.to_string(), "/work/repo")
    assert_eq(@lander_core.AbsolutePath::root().to_string(), "/")
    }

    AbsolutePathError

    pub suberror AbsolutePathError {
    InvalidAbsolutePath(text~ : String, rule~ : AbsolutePathRule)
    } derive(
    Debug
    )

    A path that cannot identify an absolute location.

    AbsolutePathError::message

    fn AbsolutePathError::message(self : AbsolutePathError) -> String

    Describe the input and the broken rule for people.

    AppNameError

    pub suberror AppNameError {
    InvalidAppName(text~ : String, rule~ : AppNameRule)
    } derive(
    Debug
    )

    A text that is not a valid application name. Callers can match the cases, but only AppName::parse makes them.

    AppNameError::message

    fn AppNameError::message(self : AppNameError) -> String

    Text for people that describes the fault.

    EnvPrefixError

    pub suberror EnvPrefixError {
    InvalidEnvPrefix(text~ : String, rule~ : EnvPrefixRule)
    } derive(
    Debug
    )

    A text that is not an environment-variable name. Callers can match the cases, but only EnvPrefix::parse makes them.

    EnvPrefixError::message

    fn EnvPrefixError::message(self : EnvPrefixError) -> String

    Text for people that describes the fault.

    TimeLimitError

    pub suberror TimeLimitError {
    InvalidTimeLimit(input~ : String, rule~ : TimeLimitRule)
    } derive(
    Debug
    )

    An input that is not a valid time limit. Callers can match the cases, but only the constructors of TimeLimit make them.

    TimeLimitError::message

    fn TimeLimitError::message(self : TimeLimitError) -> String

    Text for people that describes the fault.

    AbsolutePath

    pub struct AbsolutePath {
    // private fields
    } derive(Eq,
    Debug
    )

    An absolute location, with / separators for Windows forms. A Windows drive root and a UNC root are accepted too. This type suits both working directories and content roots without claiming that the location exists.

    AbsolutePath::equal

    AbsolutePath::not_equal

    fn AbsolutePath::not_equal(x : AbsolutePath, y : AbsolutePath) -> Bool

    AbsolutePath::output

    fn AbsolutePath::output(self : AbsolutePath, logger : &Logger) -> Unit

    AbsolutePath::parse

    fn AbsolutePath::parse(text : String) -> AbsolutePath raise AbsolutePathError

    Parse at a path boundary, converting Windows separators once.

    AbsolutePath::root

    The root used by hosts without a process working directory.

    AbsolutePath::to_string

    fn AbsolutePath::to_string(self : AbsolutePath) -> String

    AbsolutePathRule

    pub enum AbsolutePathRule {
    Empty
    NotAbsolute
    MissingUncServerOrShare
    } derive(Eq,
    Debug
    )

    A rule of an absolute path.

    AbsolutePathRule::equal

    AbsolutePathRule::not_equal

    fn AbsolutePathRule::not_equal(x : AbsolutePathRule, y : AbsolutePathRule) -> Bool

    AppName

    pub struct AppName {
    // private fields
    } derive(Eq, Hash,
    Debug
    )

    The name of an application. It starts with an ASCII letter, and holds only ASCII letters, digits, - and _. So it is safe in file and directory names, and it gives a valid environment-variable prefix.
    impl Show for AppName

    AppName::equal

    fn AppName::equal(AppName, AppName) -> Bool

    AppName::hash

    fn AppName::hash(self : AppName) -> Int

    AppName::hash_combine

    fn AppName::hash_combine(AppName, Hasher) -> Unit

    AppName::not_equal

    fn AppName::not_equal(x : AppName, y : AppName) -> Bool

    AppName::output

    fn AppName::output(self : AppName, logger : &Logger) -> Unit

    AppName::parse

    fn AppName::parse(text : String) -> AppName raise AppNameError

    Parse an application name, for example acme or my-tool.

    AppName::to_repr

    AppName::to_string

    fn AppName::to_string(self : AppName) -> String

    AppNameRule

    pub enum AppNameRule {
    Empty
    FirstNotAsciiLetter
    CharNotAllowed(Char)
    } derive(Eq,
    Debug
    )

    The rule of an application name that a text breaks.

    AppNameRule::equal

    fn AppNameRule::equal(AppNameRule, AppNameRule) -> Bool

    AppNameRule::not_equal

    fn AppNameRule::not_equal(x : AppNameRule, y : AppNameRule) -> Bool

    EnvPrefix

    pub struct EnvPrefix {
    // private fields
    } derive(Eq, Hash,
    Debug
    )

    A variable prefix starting with an upper-case ASCII letter or underscore, followed by upper-case ASCII letters, digits and underscores.
    impl Show for EnvPrefix

    EnvPrefix::equal

    fn EnvPrefix::equal(EnvPrefix, EnvPrefix) -> Bool

    EnvPrefix::from_app_name

    fn EnvPrefix::from_app_name(name : AppName) -> EnvPrefix

    Derive the default prefix from a valid application name. This cannot fail.

    EnvPrefix::hash

    fn EnvPrefix::hash(self : EnvPrefix) -> Int

    EnvPrefix::hash_combine

    fn EnvPrefix::hash_combine(EnvPrefix, Hasher) -> Unit

    EnvPrefix::not_equal

    fn EnvPrefix::not_equal(x : EnvPrefix, y : EnvPrefix) -> Bool

    EnvPrefix::output

    fn EnvPrefix::output(self : EnvPrefix, logger : &Logger) -> Unit

    EnvPrefix::parse

    fn EnvPrefix::parse(text : String) -> EnvPrefix raise EnvPrefixError

    Parse an environment-variable prefix, for example ACME or MY_TOOL.

    EnvPrefix::to_string

    fn EnvPrefix::to_string(self : EnvPrefix) -> String

    EnvPrefixRule

    pub enum EnvPrefixRule {
    Empty
    LowerCaseLetter(Char)
    FirstNotLetterOrUnderscore
    CharNotAllowed(Char)
    } derive(Eq,
    Debug
    )

    The rule of an environment-variable prefix that a text breaks.

    EnvPrefixRule::equal

    EnvPrefixRule::not_equal

    fn EnvPrefixRule::not_equal(x : EnvPrefixRule, y : EnvPrefixRule) -> Bool

    TimeLimit

    pub struct TimeLimit {
    // private fields
    } derive(Compare, Eq, Hash,
    Debug
    )

    A positive amount of time, from 1 millisecond to 2 147 483 647 milliseconds (the largest Int, about 24.8 days). This is the range of the timeouts of moonbitlang/async, so to_millis can give the value to it.
    impl Show for TimeLimit

    TimeLimit::compare

    fn TimeLimit::compare(TimeLimit, TimeLimit) -> Int

    TimeLimit::equal

    fn TimeLimit::equal(TimeLimit, TimeLimit) -> Bool

    TimeLimit::hash

    fn TimeLimit::hash(self : TimeLimit) -> Int

    TimeLimit::hash_combine

    fn TimeLimit::hash_combine(TimeLimit, Hasher) -> Unit

    TimeLimit::millis

    fn TimeLimit::millis(count : Int) -> TimeLimit raise TimeLimitError

    Make a time limit of count milliseconds.

    TimeLimit::not_equal

    fn TimeLimit::not_equal(x : TimeLimit, y : TimeLimit) -> Bool

    TimeLimit::op_ge

    fn TimeLimit::op_ge(x : TimeLimit, y : TimeLimit) -> Bool

    TimeLimit::op_gt

    fn TimeLimit::op_gt(x : TimeLimit, y : TimeLimit) -> Bool

    TimeLimit::op_le

    fn TimeLimit::op_le(x : TimeLimit, y : TimeLimit) -> Bool

    TimeLimit::op_lt

    fn TimeLimit::op_lt(x : TimeLimit, y : TimeLimit) -> Bool

    TimeLimit::output

    fn TimeLimit::output(self : TimeLimit, logger : &Logger) -> Unit

    TimeLimit::parse

    fn TimeLimit::parse(text : String) -> TimeLimit raise TimeLimitError

    Parse a time limit from its text form: one or more parts, each a decimal number, an optional space and a unit, from the largest unit to the smallest. For example 9ms, 10 seconds, 1.5s and 2m30s. The units are ms, s, m and h, and the lower-case words millisecond, second, minute and hour, each also with a final s. The checks of the shape come first, then the checks of the unit order, then the checks of the value.

    TimeLimit::seconds

    fn TimeLimit::seconds(count : Int) -> TimeLimit raise TimeLimitError

    Make a time limit of count seconds.

    TimeLimit::to_millis

    fn TimeLimit::to_millis(self : TimeLimit) -> Int

    The time limit in milliseconds, from 1 to the largest Int.

    TimeLimit::to_string

    fn TimeLimit::to_string(self : TimeLimit) -> String

    TimeLimitRule

    pub enum TimeLimitRule {
    Empty
    UnexpectedSpace
    NotANumber
    MissingUnit
    UnknownUnit(String)
    UnitsOutOfOrder
    RepeatedUnit
    NotWholeMilliseconds
    NotPositive
    AboveLargest
    } derive(Eq,
    Debug
    )

    The rule of a time limit that an input breaks.

    TimeLimitRule::equal

    TimeLimitRule::not_equal

    fn TimeLimitRule::not_equal(x : TimeLimitRule, y : TimeLimitRule) -> Bool