Skip to main content

Rust SDK

The Rust SDK for writing TaskDaemon handlers.

Installation

[dependencies]
taskdaemon-handler = "0.1"
serde_json = "1.0"

Basic Usage

use taskdaemon_handler::{run, success, Task};
use serde_json::json;

fn main() {
    run(|task: Task| {
        let name = task.task_data
            .get("name")
            .and_then(|v| v.as_str())
            .unwrap_or("World");
        
        success(json!({
            "message": format!("Hello, {}!", name)
        }))
    });
}

Task Struct

#[derive(Deserialize)]
pub struct Task {
    pub task_id: String,
    pub task_type: String,
    pub task_data: serde_json::Value,
    pub attempt: u32,  // Current attempt (starts at 0)
}

Result Functions

use taskdaemon_handler::{success, error, Result};
use serde_json::json;

// Success with result
return success(json!({"result": "value"}));

// Non-retryable error
return error("invalid input", false);

// Retryable error
return error("service unavailable", true);
The Result enum:
#[derive(Serialize)]
#[serde(tag = "status", rename_all = "lowercase")]
pub enum Result<T: Serialize> {
    Success { result: T },
    Error { error: String, retryable: bool },
}

Complete Example

use taskdaemon_handler::{run, success, error, Task, Result};
use serde_json::{json, Value};
use base64::{Engine, engine::general_purpose::STANDARD};
use image::{ImageFormat, imageops::FilterType};
use std::io::Cursor;

fn resize_image(task: Task) -> Result<Value> {
    let url = match task.task_data.get("image_url").and_then(|v| v.as_str()) {
        Some(u) => u,
        None => return error("missing image_url", false),
    };
    
    let width = match task.task_data.get("width").and_then(|v| v.as_u64()) {
        Some(w) => w as u32,
        None => return error("missing width", false),
    };
    
    let height = match task.task_data.get("height").and_then(|v| v.as_u64()) {
        Some(h) => h as u32,
        None => return error("missing height", false),
    };

    // Download
    let response = match reqwest::blocking::get(url) {
        Ok(r) => r,
        Err(e) => return error(e.to_string(), true),  // Retryable
    };
    
    let bytes = match response.bytes() {
        Ok(b) => b,
        Err(e) => return error(e.to_string(), true),
    };

    // Decode and resize
    let img = match image::load_from_memory(&bytes) {
        Ok(i) => i,
        Err(e) => return error(e.to_string(), false),
    };
    
    let resized = img.resize_exact(width, height, FilterType::Lanczos3);

    // Encode
    let mut buf = Cursor::new(Vec::new());
    if let Err(e) = resized.write_to(&mut buf, ImageFormat::Png) {
        return error(e.to_string(), false);
    }
    
    let b64 = STANDARD.encode(buf.into_inner());

    success(json!({
        "data": b64,
        "size": [width, height],
        "format": "png"
    }))
}

fn main() {
    run(resize_image);
}

Error Handling

JSON parsing errors are automatically handled by the SDK. Your handler only receives valid Task objects. For explicit error handling, use the error function:
fn handler(task: Task) -> Result<Value> {
    let required = match task.task_data.get("required") {
        Some(v) => v,
        None => return error("missing required field", false),
    };
    
    success(json!({"value": required}))
}

Dockerfile

FROM rust:1.75 AS builder
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release

FROM debian:bookworm-slim
RUN apt-get update && apt-get install -y ca-certificates && rm -rf /var/lib/apt/lists/*
COPY --from=builder /app/target/release/handler /handler
CMD ["/handler"]
For a smaller image:
FROM rust:1.75-alpine AS builder
RUN apk add --no-cache musl-dev
WORKDIR /app
COPY Cargo.toml Cargo.lock ./
COPY src ./src
RUN cargo build --release --target x86_64-unknown-linux-musl

FROM scratch
COPY --from=builder /app/target/x86_64-unknown-linux-musl/release/handler /handler
CMD ["/handler"]

Handler Configuration

[handlers.resize]
image = "rust-handler:latest"
instances = 4
timeout = 30