Rust SDK
The official Rust SDK for Locci Scheduler provides a type-safe, async API for scheduling webhook tasks using the Tokio runtime.
Installation
Add to your Cargo.toml:
[dependencies]
locci-scheduler = "0.1.0"
tokio = { version = "1", features = ["full"] }
chrono = "0.4"
serde_json = "1.0"
Or add with Cargo toolchain
cargo add locci-scheduler
Quick Start
use locci_scheduler::{LocciScheduler, SchedulerConfig, WebhookConfig, HttpMethod}; use chrono::Utc; #[tokio::main] async fn main() -> locci_scheduler::Result<()> { let config = SchedulerConfig { base_url: "https://api.scheduler.locci.cloud".to_string(), api_token: "your-api-key".to_string(), timeout: 30000, retries: 3, }; let scheduler = LocciScheduler::new(config)?; // Create a one-time task let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: None, payload: Some(serde_json::json!({ "message": "Hello!" })), timeout_seconds: Some(30), retry_config: None, authentication: None, webhook_secret: None, }; let execute_at = Utc::now() + chrono::Duration::minutes(5); let task = scheduler.schedule_once( "My First Task".to_string(), webhook, execute_at, Some("A test task".to_string()), None, ).await?; println!("Task created: {}", task.id); Ok(()) }
Configuration
#![allow(unused)] fn main() { use locci_scheduler::SchedulerConfig; let config = SchedulerConfig { base_url: "https://api.scheduler.locci.cloud".to_string(), api_token: "your-api-key".to_string(), timeout: 30000, // Request timeout in milliseconds retries: 3, // Number of retry attempts }; let scheduler = LocciScheduler::new(config)?; }
Environment Variables
For production, use environment variables:
#![allow(unused)] fn main() { use std::env; let config = SchedulerConfig { base_url: env::var("LOCCI_SCHEDULER_URL") .unwrap_or_else(|_| "https://api.scheduler.locci.cloud".to_string()), api_token: env::var("LOCCI_SCHEDULER_TOKEN") .expect("LOCCI_SCHEDULER_TOKEN must be set"), timeout: 30000, retries: 3, }; }
Task Management
Create a Task
#![allow(unused)] fn main() { use locci_scheduler::{CreateTaskOptions, WebhookConfig, ScheduleConfig, HttpMethod}; use chrono::Utc; use std::collections::HashMap; let webhook = WebhookConfig { url: "https://your-app.com/api/reports".to_string(), method: Some(HttpMethod::Post), headers: Some({ let mut h = HashMap::new(); h.insert("X-Custom-Header".to_string(), "value".to_string()); h }), payload: Some(serde_json::json!({ "reportType": "sales", "period": "daily" })), timeout_seconds: Some(30), retry_config: None, authentication: None, webhook_secret: None, }; let task = scheduler.create_task(CreateTaskOptions { name: "Daily Report".to_string(), description: Some("Send daily sales report".to_string()), webhook, schedule: ScheduleConfig::Recurring { cron_expression: "0 9 * * *".to_string(), timezone: Some("Africa/Nairobi".to_string()), end_date: None, }, metadata: Some({ let mut m = HashMap::new(); m.insert("department".to_string(), serde_json::json!("sales")); m }), }).await?; }
Get a Task
#![allow(unused)] fn main() { let task = scheduler.get_task("task-id-here").await?; println!("Task: {} - Status: {:?}", task.name, task.status); }
List Tasks
#![allow(unused)] fn main() { use locci_scheduler::PaginationOptions; let task_list = scheduler.list_tasks(PaginationOptions { page: Some(1), per_page: Some(10), }).await?; println!("Total tasks: {}", task_list.total); for task in &task_list.tasks { println!("- {} ({:?})", task.name, task.status); } }
Update a Task
#![allow(unused)] fn main() { use locci_scheduler::UpdateTaskOptions; let updated_task = scheduler.update_task("task-id", UpdateTaskOptions { name: Some("Updated Task Name".to_string()), description: Some("New description".to_string()), ..Default::default() }).await?; }
Delete a Task
#![allow(unused)] fn main() { scheduler.delete_task("task-id").await?; }
Task Lifecycle
Pause a Task
#![allow(unused)] fn main() { let task = scheduler.pause_task("task-id").await?; println!("Task status: {:?}", task.status); // Paused }
Resume a Task
#![allow(unused)] fn main() { let task = scheduler.resume_task("task-id").await?; println!("Task status: {:?}", task.status); // Active }
Cancel a Task
#![allow(unused)] fn main() { let task = scheduler.cancel_task("task-id").await?; println!("Task status: {:?}", task.status); // Cancelled }
Manually Trigger a Task
#![allow(unused)] fn main() { scheduler.trigger_task("task-id").await?; }
Scheduling Methods
One-Time Execution
#![allow(unused)] fn main() { use chrono::{Utc, TimeZone}; let webhook = WebhookConfig { url: "https://your-app.com/api/emails/welcome".to_string(), method: Some(HttpMethod::Post), headers: None, payload: Some(serde_json::json!({ "userId": "user123" })), timeout_seconds: Some(30), retry_config: None, authentication: None, webhook_secret: None, }; let execute_at = Utc.with_ymd_and_hms(2024, 12, 25, 9, 0, 0).unwrap(); let task = scheduler.schedule_once( "Send Welcome Email".to_string(), webhook, execute_at, Some("Send welcome email to new user".to_string()), Some({ let mut m = HashMap::new(); m.insert("userId".to_string(), serde_json::json!("user123")); m }), ).await?; }
Recurring with Cron Expression
#![allow(unused)] fn main() { let task = scheduler.schedule_recurring( "Weekly Backup".to_string(), webhook, "0 2 * * 0".to_string(), // Sundays at 2 AM Some("UTC".to_string()), Some(Utc.with_ymd_and_hms(2025, 12, 31, 23, 59, 59).unwrap()), // End date Some("Weekly database backup".to_string()), None, ).await?; }
Interval-Based
#![allow(unused)] fn main() { let task = scheduler.schedule_interval( "Health Check".to_string(), webhook, 300, // Every 5 minutes (300 seconds) Some(Utc::now()), // Start immediately None, // No end date Some("Monitor API health".to_string()), None, ).await?; }
Daily at Specific Time
#![allow(unused)] fn main() { let task = scheduler.schedule_daily( "Daily Sales Report".to_string(), webhook, "09:00", // 9:00 AM (24-hour format) Some("Africa/Nairobi".to_string()), Some("Generate and send daily sales report".to_string()), None, ).await?; }
Weekly
#![allow(unused)] fn main() { let task = scheduler.schedule_weekly( "Weekly Team Standup Reminder".to_string(), webhook, 1, // Monday (0 = Sunday, 1 = Monday, etc.) "09:45", Some("Africa/Nairobi".to_string()), Some("Remind team about standup".to_string()), None, ).await?; }
Convenience Methods
#![allow(unused)] fn main() { // Every minute let task = scheduler.schedule_every_minute( "Minute Task".to_string(), webhook.clone(), None, None, ).await?; // Every hour let task = scheduler.schedule_every_hour( "Hourly Task".to_string(), webhook, None, None, ).await?; }
Webhook Configuration
Basic Webhook
#![allow(unused)] fn main() { use std::collections::HashMap; let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: Some({ let mut h = HashMap::new(); h.insert("Content-Type".to_string(), "application/json".to_string()); h.insert("X-Custom-Header".to_string(), "value".to_string()); h }), payload: Some(serde_json::json!({ "key": "value" })), timeout_seconds: Some(30), retry_config: None, authentication: None, webhook_secret: None, }; }
With Retry Configuration
#![allow(unused)] fn main() { use locci_scheduler::RetryConfig; let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: None, payload: None, timeout_seconds: Some(30), retry_config: Some(RetryConfig { max_attempts: 5, backoff_seconds: 2, backoff_multiplier: 2.0, max_backoff_seconds: 300, }), authentication: None, webhook_secret: None, }; }
Authentication Types
API Key
#![allow(unused)] fn main() { use locci_scheduler::AuthConfig; let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: None, payload: None, timeout_seconds: Some(30), retry_config: None, authentication: Some(AuthConfig::ApiKey { header_name: "X-API-Key".to_string(), key: "your-api-key-value".to_string(), }), webhook_secret: None, }; }
Bearer Token
#![allow(unused)] fn main() { let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: None, payload: None, timeout_seconds: Some(30), retry_config: None, authentication: Some(AuthConfig::BearerToken { token: "your-bearer-token".to_string(), }), webhook_secret: None, }; }
Basic Auth
#![allow(unused)] fn main() { let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: None, payload: None, timeout_seconds: Some(30), retry_config: None, authentication: Some(AuthConfig::BasicAuth { username: "user".to_string(), password: "password".to_string(), }), webhook_secret: None, }; }
HMAC Webhook Signature
For secure webhook verification:
#![allow(unused)] fn main() { let webhook = WebhookConfig { url: "https://your-app.com/webhook".to_string(), method: Some(HttpMethod::Post), headers: None, payload: Some(serde_json::json!({ "data": "sensitive" })), timeout_seconds: Some(30), retry_config: None, authentication: None, webhook_secret: Some("your-webhook-secret".to_string()), }; }
The scheduler will include an X-Locci-Signature header with an HMAC-SHA256 signature. Verify on your server:
#![allow(unused)] fn main() { use hmac::{Hmac, Mac}; use sha2::Sha256; type HmacSha256 = Hmac<Sha256>; fn verify_signature(payload: &str, signature: &str, secret: &str) -> bool { let mut mac = HmacSha256::new_from_slice(secret.as_bytes()) .expect("HMAC can take key of any size"); mac.update(payload.as_bytes()); let expected = hex::encode(mac.finalize().into_bytes()); signature == expected } }
Execution History
#![allow(unused)] fn main() { let history = scheduler.get_task_history("task-id").await?; println!("Total executions: {}", history.total); for exec in &history.executions { println!("- {:?}: {:?}", exec.executed_at, exec.status); if let Some(ref error) = exec.error_message { println!(" Error: {}", error); } } }
Billing & Subscription
Get Current Subscription
#![allow(unused)] fn main() { let subscription = scheduler.get_subscription().await?; println!("Plan: {:?}", subscription.plan); println!("Status: {}", subscription.status); }
Subscribe to a Plan
#![allow(unused)] fn main() { use locci_scheduler::SubscriptionPlan; let subscription = scheduler.subscribe( SubscriptionPlan::Pro, "254712345678".to_string(), // M-Pesa phone number ).await?; }
Get Usage
#![allow(unused)] fn main() { let usage = scheduler.get_usage().await?; println!("Tasks created: {}", usage.current_usage.tasks_created); println!("Webhook executions: {}", usage.current_usage.webhook_executions); println!("API calls: {}", usage.current_usage.api_calls); println!("Max tasks: {:?}", usage.limits.max_tasks); }
Cancel Subscription
#![allow(unused)] fn main() { let subscription = scheduler.cancel_subscription().await?; }
Error Handling
The SDK provides a comprehensive error type:
#![allow(unused)] fn main() { use locci_scheduler::SchedulerError; match scheduler.create_task(options).await { Ok(task) => println!("Task created: {}", task.id), Err(SchedulerError::AuthenticationError(msg)) => { eprintln!("Authentication failed: {}", msg); } Err(SchedulerError::ValidationError(msg)) => { eprintln!("Invalid configuration: {}", msg); } Err(SchedulerError::NotFoundError(msg)) => { eprintln!("Resource not found: {}", msg); } Err(SchedulerError::RateLimitError(msg)) => { eprintln!("Rate limit exceeded: {}", msg); } Err(SchedulerError::ApiError { status, message }) => { eprintln!("API error ({}): {}", status, message); } Err(e) => eprintln!("Unexpected error: {}", e), } }
Error Types
#![allow(unused)] fn main() { pub enum SchedulerError { AuthenticationError(String), ValidationError(String), NotFoundError(String), RateLimitError(String), ApiError { status: u16, message: String }, NetworkError(reqwest::Error), JsonError(serde_json::Error), } }
Types Reference
Task Status
#![allow(unused)] fn main() { pub enum TaskStatus { Active, Paused, Completed, Failed, Cancelled, } }
Execution Status
#![allow(unused)] fn main() { pub enum ExecutionStatus { Success, Failed, Retrying, TimedOut, } }
HTTP Methods
#![allow(unused)] fn main() { pub enum HttpMethod { Get, Post, Put, Patch, Delete, } }
Schedule Config
#![allow(unused)] fn main() { pub enum ScheduleConfig { Once { execute_at: DateTime<Utc>, }, Recurring { cron_expression: String, timezone: Option<String>, end_date: Option<DateTime<Utc>>, }, Interval { interval_seconds: u64, start_at: Option<DateTime<Utc>>, end_date: Option<DateTime<Utc>>, }, } }
Subscription Plans
#![allow(unused)] fn main() { pub enum SubscriptionPlan { Free, Starter, Pro, Business, } }
Complete Example
use locci_scheduler::{ LocciScheduler, SchedulerConfig, WebhookConfig, HttpMethod, PaginationOptions, SchedulerError, }; use chrono::Utc; use std::collections::HashMap; use std::env; #[tokio::main] async fn main() -> Result<(), Box<dyn std::error::Error>> { // Initialize client let config = SchedulerConfig { base_url: env::var("LOCCI_SCHEDULER_URL") .unwrap_or_else(|_| "https://api.scheduler.locci.cloud".to_string()), api_token: env::var("LOCCI_SCHEDULER_TOKEN") .expect("LOCCI_SCHEDULER_TOKEN must be set"), timeout: 30000, retries: 3, }; let scheduler = LocciScheduler::new(config)?; // Create webhook config let webhook = WebhookConfig { url: "https://httpbin.org/post".to_string(), method: Some(HttpMethod::Post), headers: Some({ let mut h = HashMap::new(); h.insert("Content-Type".to_string(), "application/json".to_string()); h }), payload: Some(serde_json::json!({ "message": "Hello from Locci Scheduler!", "timestamp": Utc::now().to_rfc3339() })), timeout_seconds: Some(30), retry_config: None, authentication: None, webhook_secret: None, }; // Schedule a task let task = scheduler.schedule_interval( "Test Task".to_string(), webhook, 60, // Every minute Some(Utc::now()), None, Some("Test task for demonstration".to_string()), None, ).await?; println!("Created task: {}", task.id); // List all tasks let tasks = scheduler.list_tasks(PaginationOptions { page: Some(1), per_page: Some(10), }).await?; println!("\nAll tasks ({}):", tasks.total); for t in &tasks.tasks { println!(" - {} ({:?})", t.name, t.status); } // Get usage information match scheduler.get_usage().await { Ok(usage) => { println!("\nUsage:"); println!(" Tasks created: {}", usage.current_usage.tasks_created); println!(" Webhook executions: {}", usage.current_usage.webhook_executions); println!(" Plan: {:?}", usage.subscription.plan); } Err(e) => eprintln!("Failed to get usage: {}", e), } // Pause the task let paused_task = scheduler.pause_task(&task.id).await?; println!("\nTask paused: {:?}", paused_task.status); // Resume the task let resumed_task = scheduler.resume_task(&task.id).await?; println!("Task resumed: {:?}", resumed_task.status); // Delete the task scheduler.delete_task(&task.id).await?; println!("Task deleted"); Ok(()) }
Resources
- Crates.io: https://crates.io/crates/locci-scheduler
- docs.rs: https://docs.rs/locci-scheduler
- GitHub: https://github.com/MikeTeddyOmondi/locci-scheduler/tree/main/sdk/rust
- Examples: Check the
examples/directory in the SDK - API Documentation: https://docs.scheduler.locci.cloud
- Support: support@locci.cloud