Skip to main content

Crate cinderblock_core

Crate cinderblock_core 

Source
Expand description

Core crate for the cinderblock framework — a declarative, resource-oriented application framework for Rust.

This crate provides the resource! macro, the Resource trait, CRUD operation traits (Create, Update, Destroy, ReadAction), the runtime Context, and a built-in InMemoryDataLayer for prototyping.

§The resource! macro

The resource! macro is the primary entry point for defining domain models. It accepts a declarative DSL and generates:

  • A struct with the declared attributes (derives Serialize, Deserialize, Clone, Debug).
  • A Resource trait impl with primary key metadata and the configured data layer.
  • For each action, a marker struct and the corresponding CRUD trait impl. Create and update actions also generate an input struct.
  • Extension dispatch — each declared extension receives the full DSL tokens so it can generate its own code (e.g. route handlers, SQL queries).

§DSL reference

use cinderblock_core::resource;

resource! {
    // A dotted name identifying the resource. The last segment becomes the
    // struct name; all segments are available at runtime via `Resource::NAME`.
    name = Helpdesk.Support.Ticket;

    // Optional: override the data layer. Defaults to `InMemoryDataLayer`.
    // data_layer = cinderblock_sqlx::sqlite::SqliteDataLayer;

    attributes {
        // Each attribute is `name Type` followed by either `;` or an options block.
        ticket_id Uuid {
            primary_key true;   // Marks this as the primary key (default: false).
            writable false;     // Excludes from create/update input structs (default: true).
            generated true;     // Indicates the PK is auto-generated (default: false).
            default || Uuid::new_v4();  // Closure producing a default value.
        }

        // Simple form — writable, not a primary key, no default.
        subject String;
        status TicketStatus;
    }

    actions {
        // ── Read actions ──
        //
        // A read action returns `Vec<Resource>`. It can optionally declare
        // arguments (typed query parameters) and filters.

        // Minimal read — no filters, no arguments. Arguments type is `()`.
        read all;

        // Read with a compile-time literal filter.
        read open_tickets {
            filter { status == TicketStatus::Open };
        };

        // Read with a runtime argument bound to a filter.
        // Generates a `ByStatusArguments` struct with a `status` field.
        read by_status {
            argument { status: TicketStatus };
            filter { status == arg(status) };
        };

        // Optional arguments use `Option<T>`. When `None`, the filter is
        // skipped entirely at runtime.
        read search {
            argument { status: Option<TicketStatus> };
            filter { status == arg(status) };
        };

        // ── Create actions ──
        //
        // A create action generates an input struct from the resource's
        // writable attributes and a `Create<A>` impl that builds a new
        // resource instance.

        // Accepts all writable attributes. Generates `OpenInput { subject, status }`.
        create open;

        // Restrict which fields the input struct includes.
        // Generates `AssignInput { subject }`.
        create assign {
            accept [subject];
        };

        // ── Update actions ──
        //
        // An update action fetches the resource by primary key, applies
        // changes, and persists the result. It generates an input struct
        // and an `Update<A>` impl.

        // Accepts all writable attributes.
        update edit;

        // Accept no fields from the caller, but apply a programmatic
        // mutation via `change_ref`. Multiple `change_ref` blocks are
        // applied in order.
        update close {
            accept [];
            change_ref |ticket| {
                ticket.status = TicketStatus::Closed;
            };
        };

        // ── Destroy actions ──
        //
        // A destroy action deletes the resource by primary key.
        destroy remove;
    }

    // Optional: declare extensions. Each extension module receives the
    // full resource DSL and its own configuration block, then generates
    // additional code (e.g. route handlers, SQL queries).
    extensions {
        cinderblock_json_api {
            route = { method = GET; path = "/"; action = all; };
            route = { method = POST; path = "/"; action = open; };
        };

        cinderblock_sqlx {
            table = "tickets";
        };
    }
}

§Generated items

For a resource named Helpdesk.Support.Ticket with actions open (create), close (update), open_tickets (read), and remove (destroy), the macro generates:

Generated itemKindDescription
TicketstructThe resource struct with all declared attributes
Openstruct (marker)Create action marker
OpenInputstructInput fields for the open create action
Closestruct (marker)Update action marker
CloseInputstructInput fields for the close update action
OpenTicketsstruct (marker)Read action marker
Removestruct (marker)Destroy action marker

Action names are converted to PascalCase for the marker and input struct names (e.g. open_tickets becomes OpenTickets, and its input struct would be OpenTicketsInput).

§Using the generated types

use cinderblock_core::Context;

let ctx = Context::new();

// Create
let ticket = cinderblock_core::create::<Ticket, Open>(
    OpenInput { subject: "Printer is broken".into(), status: TicketStatus::Open },
    &ctx,
).await?;

// Read (with arguments)
let open = cinderblock_core::read::<Ticket, ByStatus>(
    &ctx,
    &ByStatusArguments { status: TicketStatus::Open },
).await?;

// Read (no arguments — pass `&()`)
let all_open = cinderblock_core::read::<Ticket, OpenTickets>(&ctx, &()).await?;

// Update
let closed = cinderblock_core::update::<Ticket, Close>(
    &ticket.ticket_id,
    CloseInput {},
    &ctx,
).await?;

// Destroy
let removed = cinderblock_core::destroy::<Ticket, Remove>(
    &ticket.ticket_id,
    &ctx,
).await?;

Re-exports§

pub use serde;

Modules§

data_layer

Macros§

resource

Structs§

Context

Traits§

Create
Trait placed on a Resource specifying how to create the resource using action A.
Destroy
Marker trait for destroy actions.
PerformRead
Trait indicating that a DataLayer can perform ReadAction A.
ReadAction
Marker trait showing indicating that a struct is a read action.
Resource
Marker trait for a resource.
Update
Trait placed on a Resource specifying how to update a resource using action A.

Functions§

create
Create resource R using action A.
destroy
Destroy resource R using action A.
read
Read resource R using action A.
update
Update resource R using action A. First fetches an instance of R using the primary key.

Type Aliases§

Result