hyperware_process_lib 3.0.0

A library for writing Hyperware processes in Rust.
Documentation
package hyperware:process@1.0.0;

interface standard {

    //     ˗ˏˋ ♡ ˎˊ˗
    //    System Types
    //     ˗ˏˋ ♡ ˎˊ˗

    /// JSON is passed over Wasm boundary as a string.
    type json = string;

    /// In types passed from kernel, node-id will be a valid Hypermap entry.
    type node-id = string;

    /// Context, like a message body, is a protocol-defined serialized byte
    /// array. It is used when building a Request to save information that
    /// will not be part of a Response, in order to more easily handle
    /// ("contextualize") that Response.
    type context = list<u8>;

    record process-id {
        process-name: string,
        package-name: string,
        publisher-node: node-id,
    }

    record package-id {
        package-name: string,
        publisher-node: node-id,
    }

    record address {
        node: node-id,
        process: process-id,
    }

    record lazy-load-blob {
        mime: option<string>,
        bytes: list<u8>,
    }

    record request {
        // set in order to inherit lazy-load-blob from parent message, and if
        // expects-response is none, direct response to source of parent.
        // also carries forward certain aspects of parent message in kernel,
        // see documentation for formal spec and examples:
        // https://docs.rs/hyperware_process_lib/latest/hyperware_process_lib/struct.Request.html
        inherit: bool,
        // if some, request expects a response in the given number of seconds
        expects-response: option<u64>,
        body: list<u8>,
        metadata: option<json>,
        capabilities: list<capability>,
        // to grab lazy-load-blob, use get_blob()
    }

    record response {
        inherit: bool,
        body: list<u8>,
        metadata: option<json>,
        capabilities: list<capability>,
        // to grab lazy-load-blob, use get_blob()
    }

    /// A message can be a request or a response. Within a response, there is
    /// a result which surfaces any error that happened because of a request.
    /// A successful response will contain the context of the request it
    /// matches, if any was set.
    variant message {
        request(request),
        response(tuple<response, option<context>>),
    }

    record capability {
        issuer: address,
        params: json,
    }

    /// On-exit is a setting that determines what happens when a process
    /// panics, completes, or otherwise "ends".
    /// NOTE: requests will always have expects-response set to false by kernel.
    variant on-exit {
        none,
        restart,
        requests(list<tuple<address, request, option<lazy-load-blob>>>),
    }

    /// Send errors come from trying to send a message to another process,
    /// either locally or on another node.
    /// A message can fail by timing out, or by the node being entirely
    /// unreachable (offline or can't be found in PKI). In either case,
    /// the message is not delivered and the process that sent it receives
    /// that message back along with any assigned context and/or lazy-load-blob,
    /// and is free to handle it as it sees fit.
    /// In the local case, only timeout errors are possible and also cover the case
    /// in which a process is not running or does not exist.
    record send-error {
        kind: send-error-kind,
        target: address,
        message: message,
        lazy-load-blob: option<lazy-load-blob>,
    }

    enum send-error-kind {
        offline,
        timeout,
    }

    enum spawn-error {
        name-taken,
        no-file-at-path,
    }

    //     ˗ˏˋ ♡ ˎˊ˗
    //    System Utils
    //     ˗ˏˋ ♡ ˎˊ˗

    /// Prints to the terminal at a given verbosity level.
    /// Higher verbosity levels print more information.
    /// Level 0 is always printed -- use sparingly.
    print-to-terminal: func(verbosity: u8, message: string);

    /// Returns the address of the process.
    our: func() -> address;

    //        ˗ˏˋ ♡ ˎˊ˗
    //    Process Management
    //        ˗ˏˋ ♡ ˎˊ˗

    get-on-exit: func() -> on-exit;

    set-on-exit: func(on-exit: on-exit);

    get-state: func() -> option<list<u8>>;

    set-state: func(bytes: list<u8>);

    clear-state: func();

    spawn: func(
        // name is optional. if not provided, name will be a random u64.
        name: option<string>,
        // wasm-path must be located within package's drive
        wasm-path: string,
        on-exit: on-exit,
        // requested capabilities must be owned by the caller
        request-capabilities: list<capability>,
        // granted capabilities will be generated by the child process
        // and handed out to the indicated process-id.
        grant-capabilities: list<tuple<process-id, json>>,
        public: bool
    ) -> result<process-id, spawn-error>;

    //           ˗ˏˋ ♡ ˎˊ˗
    //    Capabilities Management
    //           ˗ˏˋ ♡ ˎˊ˗

    /// Saves the capabilities to persisted process state.
    save-capabilities: func(caps: list<capability>);

    /// Deletes the capabilities from persisted process state.
    drop-capabilities: func(caps: list<capability>);

    /// Gets all capabilities from persisted process state.
    our-capabilities: func() -> list<capability>;

    //     ˗ˏˋ ♡ ˎˊ˗
    //    Message I/O
    //     ˗ˏˋ ♡ ˎˊ˗

    /// Ingest next message when it arrives along with its source.
    /// Almost all long-running processes will call this in a loop.
    receive: func() ->
        result<tuple<address, message>, tuple<send-error, option<context>>>;

    /// Returns whether or not the current message has a blob.
    has-blob: func() -> bool;

    /// Returns the blob of the current message, if any.
    get-blob: func() -> option<lazy-load-blob>;

    /// Returns the last blob this process received.
    last-blob: func() -> option<lazy-load-blob>;

    /// Send request to target.
    send-request: func(
        target: address,
        request: request,
        context: option<context>,
        lazy-load-blob: option<lazy-load-blob>
    );

    /// Send requests to targets.
    send-requests: func(
        requests: list<tuple<address,
                             request,
                             option<context>,
                             option<lazy-load-blob>>>
    );

    /// Send response to the request currently being handled.
    send-response: func(
        response: response,
        lazy-load-blob: option<lazy-load-blob>
    );

    /// Send a single request, then block (internally) until its response. The
    /// type returned is Message but will always contain Response.
    send-and-await-response: func(
        target: address,
        request: request,
        lazy-load-blob: option<lazy-load-blob>
    ) -> result<tuple<address, message>, send-error>;
}

world lib {
    import standard;
}

world process-v1 {
    include lib;

    export init: func(our: string);
}