Thanks to visit codestin.com
Credit goes to picante.bearcove.eu

picante

async incremental queries

salsa-style memoization and dependency tracking,
built for tokio from the ground up.

๐Ÿ”ฅ

Async-First

Derived queries are natively async. No fighting the runtime โ€” picante was built for tokio from day one.

๐Ÿ“Š

Incremental

Only recompute what changed. Fine-grained dependency tracking means minimal work on each revision.

๐Ÿ’พ

Persistent

Snapshot your query graph to disk with facet. Resume where you left off โ€” no serde required.

๐ŸŽฏ

Single-Flight

Concurrent requests for the same query share one computation. No duplicate work, no wasted cycles.

๐Ÿ”„

Live Reload

Subscribe to revision changes and invalidation events. Build reactive pipelines with ease.

๐Ÿ›ก๏ธ

Panic-Safe

Query panics are caught and converted to errors. Your runtime keeps running.

Quick Start

Add picante to your Cargo.toml:

[dependencies]
picante = "0.1"
tokio = { version = "1", features = ["full"] }

Then define your database and queries:

use picante::PicanteResult;

// Define an input with a key field
#[picante::input]
pub struct SourceFile {
    #[key]
    pub path: String,
    pub content: String,
}

// Define a tracked query โ€” automatically async and memoized
// Use the combined trait (DatabaseTrait) instead of listing Has* traits
#[picante::tracked]
pub async fn line_count<DB: DatabaseTrait>(
    db: &DB,
    file: SourceFile,
) -> PicanteResult<usize> {
    let content = file.content(db)?;
    Ok(content.lines().count())
}

// Wire everything together
// The macro generates DatabaseTrait with all ingredient bounds
#[picante::db(inputs(SourceFile), tracked(line_count))]
pub struct Database {}

#[tokio::main]
async fn main() -> PicanteResult<()> {
    let db = Database::new();

    // Create an input
    let file = SourceFile::new(&db, "main.rs".into(), "fn main() {\n}\n".into())?;

    // Query is computed and cached
    assert_eq!(line_count(&db, file).await?, 2);

    Ok(())
}

Singleton Inputs

Inputs without a #[key] field are treated as singletons โ€” only one instance exists:

#[picante::input]
pub struct Config {
    pub debug: bool,
    pub timeout: u64,
}

#[picante::db(inputs(Config))]
pub struct Database {}

fn main() -> PicanteResult<()> {
    let db = Database::new();

    // Set the singleton
    Config::set(&db, true, 30)?;

    // Get returns Option (None if not set)
    let config = Config::get(&db)?.unwrap();
    assert!(config.debug);

    Ok(())
}

Custom Trait Name

Use db_trait(Name) to customize the generated combined trait name:

// Generates trait named `Db` instead of `AppDatabaseTrait`
#[picante::db(inputs(SourceFile), db_trait(Db))]
pub struct AppDatabase {}

fn use_db<DB: Db>(db: &DB) { /* ... */ }

Ready to spice up your query graph?

Check out the guide or dive into the API docs.