sans

Crates.iosans
lib.rssans
version0.1.0-alpha.4
created_at2025-10-09 04:38:36.667474+00
updated_at2025-10-13 18:44:02.50212+00
descriptionComposable coroutine-based programming library for sans-io
homepage
repositoryhttps://github.com/yiblet/sans
max_upload_size
id1875030
size199,038
Shalom Yiblet (yiblet)

documentation

https://docs.rs/sans

README

sans, composable coroutine-based programming

LICENSE Build Status crates.io Version Minimum rustc version

sans is a coroutine combinators library for building composable, resumable computations in Rust. Build pipelines that yield, resume, and compose beautifully.

What are coroutine combinators? Instead of writing complex state machines with explicit state tracking, you compose small, focused functions into pipelines. Each coroutine can yield intermediate results, maintain state, and pass control to the next coroutine. The result is code that's easier to write, test, and reuse - all with compile-time type safety and zero-cost abstractions.

Example

Interactive calculator that maintains state across inputs:

use sans::prelude::*;

// Build a stateful calculator that accumulates results
let mut total = 0_i64;
let calculator = init_repeat(0_i64, move |delta: i64| {
    total += delta;
    total
})
.map_input(|cmd: &str| -> i64 {
    let mut parts = cmd.split_whitespace();
    let op = parts.next().expect("operation");
    let amount: i64 = parts.next().expect("amount").parse().expect("number");
    match op {
        "add" => amount,
        "sub" => -amount,
        _ => panic!("unknown operation"),
    }
})
.map_yield(|value: i64| format!("total={}", value));

// Execute the pipeline
let (initial, mut coro) = calculator.init().unwrap_yielded();
println!("{}", initial);  // "total=0"

println!("{}", coro.next("add 5").unwrap_yielded());   // "total=5"
println!("{}", coro.next("sub 3").unwrap_yielded());   // "total=2"
println!("{}", coro.next("add 10").unwrap_yielded());  // "total=12"

Chained pipeline with transformation:

use sans::prelude::*;

let pipeline = init_once(10, |x: i32| x * 2)
    .map_yield(|x| x + 5)
    .chain(once(|x: i32| x * 3))
    .map_return(|x| format!("Result: {}", x));

let result = handle(pipeline, |output| {
    println!("Step: {}", output);
    output  // Pass through
});

println!("{}", result);  // "Result: 45"

Why sans?

Build stateful, resumable pipelines for:

  • Interactive Protocols - REPLs, network protocols, multi-step wizards
  • Streaming Pipelines - Data processing, stream transformations, ETL
  • State Machines - Protocol implementations, workflow engines, game AI
  • Incremental Computation - Pausable async workflows, cooperative multitasking

Key benefits:

  • Composable - Build complex pipelines from simple, reusable coroutines
  • Type-safe - Compiler ensures coroutines compose correctly
  • Zero-cost - No allocations in core combinators, stack-based state machines
  • Safe - #![forbid(unsafe_code)], no manual state management bugs
  • Concurrent - Run multiple coroutines concurrently with join

Installation

Add to your Cargo.toml:

[dependencies]
sans = "0.1.0-alpha.2"
use sans::prelude::*;

Requirements: Rust 1.85+

Documentation

Full API Documentation - Complete reference for all types, traits, and functions

Key modules:

  • sans::build - Create coroutines (once, repeat, init_once)
  • sans::compose - Combine coroutines (chain, map_input, map_yield)
  • sans::concurrent - Concurrent execution (join, poll)
  • sans::run - Execute pipelines (handle, handle_async)

License: See LICENSE | Contributing: TBD

Commit count: 0

cargo fmt