preloader 0.1.0

Asynchronous data preloader library
Documentation

Preloader

Crates.io Documentation License: MIT

A high-performance asynchronous data preloader library for Rust that provides efficient caching and concurrent access patterns.

Features

  • 🚀 Asynchronous Loading: Load data asynchronously using Rust's Future trait
  • 💾 Smart Caching: Once loaded, data is cached in memory for instant access
  • 🔒 Thread Safe: Safe concurrent access across multiple threads
  • 📊 State Management: Clear state-based behavior (Idle, Start, Loading, Loaded)
  • ⚡ Non-blocking: Optional non-blocking data retrieval with try_get()
  • 🔄 Idempotent: Multiple load calls are safely ignored after the first one

Quick Start

Add this to your Cargo.toml:

[dependencies]
preloader = "0.1.0"
tokio = { version = "1.0", features = ["full"] }

Basic Usage

use preloader::Preloader;
use tokio;

#[tokio::main]
async fn main() {
    let preloader = Preloader::new();
    
    // Start loading data asynchronously
    preloader.load(async {
        // Simulate expensive operation (network request, file I/O, etc.)
        tokio::time::sleep(tokio::time::Duration::from_secs(1)).await;
        "expensive data".to_string()
    }).await;
    
    // Block until data is ready
    match preloader.get().await {
        Ok(data) => println!("Data loaded: {}", data),
        Err(e) => println!("Error: {}", e),
    }
    
    // Non-blocking access (returns immediately if ready)
    match preloader.try_get() {
        Ok(data) => println!("Immediate access: {}", data),
        Err(e) => println!("Not ready yet: {}", e),
    }
}

Advanced Usage

use preloader::Preloader;
use std::sync::Arc;
use tokio;

#[tokio::main]
async fn main() {
    let preloader = Arc::new(Preloader::new());
    
    // Start loading
    preloader.load(async {
        // Simulate database query
        tokio::time::sleep(tokio::time::Duration::from_millis(500)).await;
        vec![1, 2, 3, 4, 5]
    }).await;

    // Multiple concurrent consumers
    let mut handles = vec![];
    for i in 0..5 {
        let preloader = Arc::clone(&preloader);
        handles.push(tokio::spawn(async move {
            let data = preloader.get().await.unwrap();
            println!("Consumer {} got: {:?}", i, data);
        }));
    }

    // Wait for all consumers
    for handle in handles {
        handle.await.unwrap();
    }
}

API Reference

Preloader<T>

The main preloader struct that handles asynchronous data loading and caching.

Methods

  • new() -> Preloader<T> - Create a new preloader instance
  • load(future) -> () - Start loading data asynchronously
  • get() -> Result<&T, PreloaderError> - Get data (blocks until ready)
  • try_get() -> Result<&T, PreloaderError> - Try to get data (non-blocking)

Error Types

#[derive(Debug, thiserror::Error)]
pub enum PreloaderError {
    #[error("Preloader is not loaded")]
    NotLoaded,
    #[error("Preloader is loading")]
    Loading,
    #[error("Failed to receive data: {0}")]
    ReceiveError(String),
}

Performance Characteristics

  • Memory Overhead: Minimal - only stores the loaded data and state
  • Concurrency: Excellent - supports unlimited concurrent readers
  • Latency: Near-zero for cached data access
  • Thread Safety: Full Send + Sync implementation

Use Cases

  • Configuration Loading: Load app configuration once, access everywhere
  • Database Connections: Preload connection pools
  • File Caching: Cache frequently accessed files
  • API Response Caching: Cache external API responses
  • Resource Initialization: Initialize heavy resources on startup

Examples

Configuration Loading

use preloader::Preloader;
use serde::{Deserialize, Serialize};

#[derive(Debug, Serialize, Deserialize)]
struct Config {
    database_url: String,
    api_key: String,
    port: u16,
}

async fn load_config() -> Config {
    let preloader = Preloader::new();
    
    preloader.load(async {
        // Load from file or environment
        tokio::fs::read_to_string("config.json").await.unwrap();
        serde_json::from_str(&content).unwrap()
    }).await;
    
    preloader.get().await.unwrap().clone()
}

Database Connection Pool

use preloader::Preloader;
use sqlx::PgPool;

async fn create_connection_pool() -> PgPool {
    let preloader = Preloader::new();
    
    preloader.load(async {
        PgPool::connect("postgresql://user:pass@localhost/db").await.unwrap()
    }).await;
    
    preloader.get().await.unwrap().clone()
}

Contributing

Contributions are welcome! Please feel free to submit a Pull Request. For major changes, please open an issue first to discuss what you would like to change.

Development Setup

git clone https://github.com/yourusername/preloader.git
cd preloader
cargo test
cargo doc --open

License

This project is licensed under the MIT License - see the LICENSE file for details.

Acknowledgments

  • Built with Tokio for async runtime
  • Uses thiserror for error handling
  • Inspired by modern caching patterns and concurrent programming best practices

Made with ❤️ in Rust