honker 0.1.0

SQLite-native task runtime: durable queues, streams, pub/sub, and scheduler. Ergonomic Rust wrapper over honker-core.
Documentation

honker (Rust)

Ergonomic Rust binding for Honker — durable queues, streams, pub/sub, and scheduler on SQLite.

Install

[dependencies]
honker = "0.1"
serde_json = "1"

No separate extension download needed — this crate compiles honker-core in directly and registers every honker_* SQL function on the connection it opens.

Quick start

use honker::{Database, EnqueueOpts, QueueOpts};
use serde_json::json;

fn main() -> honker::Result<()> {
    let db = Database::open("app.db")?;
    let q = db.queue("emails", QueueOpts::default());

    q.enqueue(&json!({"to": "alice@example.com"}), EnqueueOpts::default())?;

    if let Some(job) = q.claim_one("worker-1")? {
        let body: serde_json::Value = job.payload_as()?;
        // ... send_email(body) ...
        job.ack()?;
    }
    Ok(())
}

Why this crate + when to use honker-core directly

honker-core is the low-level Rust crate shared across every Honker binding (PyO3, napi-rs, this). It exposes the building blocks: open_conn, attach_honker_functions, Writer, Readers, SharedWalWatcher.

This crate adds ergonomics: typed Queue / Job, Database::open that bundles bootstrap + pragmas, serde-based payloads, and a clean error type.

Use honker-core directly when you're writing a binding for another language; use honker when you're writing an application in Rust.

API

Database::open(path)Result<Database>

Opens SQLite, applies PRAGMAs, registers honker_* scalar functions, bootstraps schema.

Database::queue(name, QueueOpts)Queue<'_>

QueueOpts { visibility_timeout_s: 300, max_attempts: 3 } by default.

Queue::enqueue(&payload, EnqueueOpts)Result<i64>

EnqueueOpts { delay, run_at, priority, expires } — all optional. Returns new row id.

Queue::claim_batch(worker_id, n) / Queue::claim_one(worker_id)

Atomic claim. Returns Vec<Job> / Option<Job>.

Job::ack / Job::retry(delay_s, error) / Job::fail(error) / Job::heartbeat(extend_s)

Lifecycle methods. Each returns Result<bool>true iff the claim was still valid.

Job::payload_as::<T>()

Deserialize the payload into a serde type:

#[derive(serde::Deserialize)]
struct Email { to: String, subject: String }

let email: Email = job.payload_as()?;

Database::notify(channel, &payload)

Fire a pg_notify-style signal. Payload is JSON-encoded automatically.

Database::conn()&rusqlite::Connection

Escape hatch for advanced queries (raw SQL, joining to your business tables, etc).

What's not here yet

  • Listen / WAL-based async wake (will need tokio integration; planning)
  • Streams (typed Stream::publish / Stream::subscribe)
  • Scheduler (typed Scheduler::add / run)

All available via raw SQL on db.conn(). Idiomatic wrappers in progress.

Testing

cd packages/honker-rs
cargo test