Skip to main content

Crate cuttlefish_sdk

Crate cuttlefish_sdk 

Source
Expand description

Guest-side library for writing cuttlefish proc-blocks.

A block is a state machine that the host drives. It never calls the host and waits; it returns a Command saying what it wants, and the host — having done that thing — steps it again with an Event. See cuttlefish_abi for why control is inverted, and what that buys: chiefly that cancellation needs no cooperation from the guest, because the host simply stops stepping.

This crate exists so block authors do not hand-write that inversion. Implement Block, call export_block!, and the macro emits the raw wasm exports the host expects.

§Writing a block

use cuttlefish_sdk::{Block, Command, Event};

#[derive(Default)]
struct Shout;

impl Block for Shout {
    fn start(&mut self, input: serde_json::Value) -> Command {
        match input.get("path").and_then(|v| v.as_str()) {
            Some(path) => Command::Open { path: path.to_string() },
            None => Command::Fail {
                code: "schema_validation_failed".into(),
                message: "input needs a string `path`".into(),
            },
        }
    }

    fn step(&mut self, event: Event) -> Command {
        match event {
            Event::Opened { handle, len, .. } => Command::Slice { handle, offset: 0, len },
            Event::Sliced { text, .. } => Command::Done {
                result: serde_json::json!({ "shouted": text.to_uppercase() }),
            },
            other => Command::Fail {
                code: "unexpected_event".into(),
                message: format!("{other:?}"),
            },
        }
    }
}

// A real block adds this to emit the wasm exports:
//     cuttlefish_sdk::export_block!(Shout);

Because a block is an ordinary Rust type, it can be unit-tested natively with no wasm involved: construct it, call start, then feed it the events its commands would produce. Only the boundary itself needs a wasm harness.

§Memory ownership across the boundary

Values handed to the host are leaked on purpose. The host reads them immediately after the call returns, and the entire instance is destroyed when the job ends, so there is nothing to reclaim and no cross-language allocator coordination to get wrong.

Do not “fix” this by freeing. The host would then read freed memory, and on wasm that is a silent wrong answer rather than a segfault — the linear memory is still perfectly valid to read, it just no longer holds what anyone thinks.

Macros§

export_block
Emit the wasm exports for a Block implementation.

Structs§

Desc
How a guest hands a (pointer, length) pair back to the host.
Signature
What a block accepts and produces.

Enums§

Command
What a guest asks the host to do, returned from its init/step exports.
Event
What the host feeds back into the guest’s step export after carrying out a Command.
ImageOperation
One transformation Command::ImageOp can apply.
MediaKind
What kind of thing a handle refers to, reported by Event::Opened.
TokenAction
A guest’s verdict on each streamed token, returned from its on_token export.
Ty
The shape of a value flowing through a pipeline.

Traits§

Block
What a proc-block author implements.

Type Aliases§

Handle
A job-scoped reference to something the host holds open for a guest.