diff --git a/.github/workflows/ci-rust.yml b/.github/workflows/ci-rust.yml index 17767174..287bac0e 100644 --- a/.github/workflows/ci-rust.yml +++ b/.github/workflows/ci-rust.yml @@ -48,7 +48,7 @@ jobs: # depending on it. See crates/taskito-core/BINDING_CONTRACT.md. run: | set -euo pipefail - for crate in taskito-core taskito-workflows taskito-mesh; do + for crate in taskito taskito-core taskito-workflows taskito-mesh; do # Capture first so a `cargo tree` failure aborts (set -e) instead of # being swallowed by the pipe and silently passing the check. tree_output="$(cargo tree -p "$crate" -e normal --all-features)" @@ -153,7 +153,7 @@ jobs: name: Publish Readiness (Rust crates) runs-on: ubuntu-latest env: - CRATES: -p taskito-core -p taskito-workflows -p taskito-mesh + CRATES: -p taskito -p taskito-core -p taskito-workflows -p taskito-mesh steps: - uses: actions/checkout@v7.0.1 with: @@ -203,7 +203,7 @@ jobs: # Cargo's own index format, no rate limits, no User-Agent policy # (the API 403s UA-less requests). Index path scheme for names of # 4+ chars: /{first-two}/{next-two}/{name}, lowercase. - for crate in taskito-core taskito-workflows taskito-mesh; do + for crate in taskito taskito-core taskito-workflows taskito-mesh; do prefix="${crate:0:2}/${crate:2:2}" # Fail closed: only an explicit 404 means "no baseline" — a transient # network error must not silently skip the semver check. Retries @@ -221,6 +221,12 @@ jobs: esac done + - name: Semver check taskito + if: steps.baseline.outputs.taskito == 'true' + uses: obi1kenobi/cargo-semver-checks-action@v2 + with: + package: taskito + - name: Semver check taskito-core if: steps.baseline.outputs.taskito-core == 'true' uses: obi1kenobi/cargo-semver-checks-action@v2 diff --git a/.github/workflows/publish-crates.yml b/.github/workflows/publish-crates.yml index d4134d9d..dac0a294 100644 --- a/.github/workflows/publish-crates.yml +++ b/.github/workflows/publish-crates.yml @@ -37,7 +37,7 @@ env: # taskito-mesh both depend on taskito-core, so the set goes up together and # cargo works out the upload order and the index waits itself. Every other # workspace member is `publish = false` — binding shells and binaries. - CRATES: taskito-core taskito-workflows taskito-mesh + CRATES: taskito taskito-core taskito-workflows taskito-mesh jobs: publish: diff --git a/Cargo.lock b/Cargo.lock index 1b80ebba..e52c87d8 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -3154,6 +3154,13 @@ version = "0.13.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "adb6935a6f5c20170eeceb1a3835a49e12e19d792f6dd344ccc76a985ca5a6ca" +[[package]] +name = "taskito" +version = "0.22.0" +dependencies = [ + "taskito-core", +] + [[package]] name = "taskito-core" version = "0.22.0" diff --git a/Cargo.toml b/Cargo.toml index 7e2e47ee..de8763c0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,5 +1,5 @@ [workspace] -members = ["crates/taskito-core", "crates/taskito-python", "crates/taskito-node", "crates/taskito-java", "crates/taskito-workflows", "crates/taskito-mesh", "crates/taskito-tui", "crates/taskito-server"] +members = ["crates/taskito", "crates/taskito-core", "crates/taskito-python", "crates/taskito-node", "crates/taskito-java", "crates/taskito-workflows", "crates/taskito-mesh", "crates/taskito-tui", "crates/taskito-server"] resolver = "2" # Shared crate metadata — every member inherits via `x.workspace = true`, so a diff --git a/crates/taskito/Cargo.toml b/crates/taskito/Cargo.toml new file mode 100644 index 00000000..a0d42430 --- /dev/null +++ b/crates/taskito/Cargo.toml @@ -0,0 +1,22 @@ +[package] +name = "taskito" +version.workspace = true +edition.workspace = true +license.workspace = true +repository.workspace = true +rust-version.workspace = true +description = "Embeddable task queue for Rust — the `taskito` entry point, re-exporting taskito-core" +keywords = ["task-queue", "jobs", "scheduler", "background", "worker"] +categories = ["asynchronous", "database"] +readme = "README.md" + +[features] +default = [] +# Forwarded verbatim so `cargo add taskito --features postgres` behaves exactly +# as the same flag on taskito-core does. +postgres = ["taskito-core/postgres"] +redis = ["taskito-core/redis"] +push-dispatch = ["taskito-core/push-dispatch"] + +[dependencies] +taskito-core = { workspace = true } diff --git a/crates/taskito/LICENSE b/crates/taskito/LICENSE new file mode 100644 index 00000000..f75a1e74 --- /dev/null +++ b/crates/taskito/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2026 Pratyush Sharma + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/crates/taskito/README.md b/crates/taskito/README.md new file mode 100644 index 00000000..c2ddd83e --- /dev/null +++ b/crates/taskito/README.md @@ -0,0 +1,75 @@ +# taskito + +Embeddable task queue for Rust: durable jobs on SQLite (default), PostgreSQL, or +Redis, a scheduler with retries, rate limits, circuit breakers, cron periodics, +and pub/sub fan-out — and a native worker that runs your handlers. + +This crate is the `taskito` entry point, matching the name the Python, Node, and +Java SDKs already use. It is a re-export of +[`taskito-core`](https://crates.io/crates/taskito-core) and adds nothing of its +own: `taskito::Worker` and `taskito_core::Worker` are the same type, so the two +can be mixed freely and either name works in a dependency graph that contains +both. + +Reach for `taskito-core` directly if you prefer the explicit name; everything is +documented there. + +## Quick start + +```rust,no_run +use taskito::{now_millis, Job, NewJob, SqliteStorage, Storage, StorageBackend, Worker}; + +fn main() -> taskito::Result<()> { + let storage = StorageBackend::Sqlite(SqliteStorage::new("taskito.db")?); + + // A worker executes registered handlers for dequeued jobs. + let handle = Worker::new(storage.clone()) + .num_workers(4) + .register("greet", |job: &Job| { + println!("hello, {}!", String::from_utf8_lossy(&job.payload)); + Ok(None) + }) + .spawn()?; + + // Producers enqueue jobs — from this process or any other. + storage.enqueue(NewJob { + queue: "default".to_string(), + task_name: "greet".to_string(), + payload: b"world".to_vec(), + priority: 0, + scheduled_at: now_millis(), + max_retries: 3, + timeout_ms: 30_000, + unique_key: None, + metadata: None, + notes: None, + depends_on: vec![], + expires_at: None, + result_ttl_ms: None, + namespace: None, + })?; + + std::thread::sleep(std::time::Duration::from_millis(500)); + handle.shutdown() +} +``` + +## Features + +| Feature | Effect | +| --- | --- | +| *(default)* | SQLite storage | +| `postgres` | PostgreSQL storage | +| `redis` | Redis storage | +| `push-dispatch` | event-driven scheduler wakeups instead of polling | + +Each forwards to the identically named feature on `taskito-core`. + +## Companion crates + +- [`taskito-workflows`](https://crates.io/crates/taskito-workflows) — DAG workflows +- [`taskito-mesh`](https://crates.io/crates/taskito-mesh) — decentralized mesh scheduling + +## License + +MIT diff --git a/crates/taskito/src/lib.rs b/crates/taskito/src/lib.rs new file mode 100644 index 00000000..7545f26c --- /dev/null +++ b/crates/taskito/src/lib.rs @@ -0,0 +1,10 @@ +#![doc = include_str!("../README.md")] + +// The whole crate is a re-export: `taskito::X` and `taskito_core::X` are the +// same item, so there is exactly one definition and one set of docs to keep +// current. Anything added to the core root appears here without an edit. +pub use taskito_core::*; + +// Also re-exported under its own name, so code that already spells out +// `taskito_core::` keeps compiling when it depends only on this crate. +pub use taskito_core;