statum 0.1.6

Compile-time state machine magic for Rust: Zero-boilerplate typestate patterns with automatic transition validation
Documentation
# statum
A zero-boilerplate library for finite-state machines in Rust, with compile-time state transition validation.
## Overview
The typestate pattern lets you encode state machines at the type level, making invalid state transitions impossible at compile time. This crate makes implementing typestates effortless through three attributes:
- `#[state]` - Define your states
- `#[context]` - Create your state machine

## Installation
Add this to your `Cargo.toml`:
```toml
[dependencies]
statum = "0.1.6"
```
## Quick Start
Here's a minimal example of a task processor:
```rust
use statum::{state, context};

#[state]
pub enum TaskState {
    New,
    InProgress,
    Complete,
}

#[context]
struct Task<S: TaskState> {
    id: String,
    data: Vec<u32>,
}

impl Task<New> {
    fn start(self) -> Result<Task<InProgress>> {
        // Use .into_context() to transition states
        Ok(self.into_context())
    }
}

impl Task<InProgress> {
    async fn process(self) -> Result<Task<Complete>> {
        // Do some work...
        Ok(self.into_context())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let task = Task::new("task-1".to_owned(), vec![])
        .start()?
        .process()
        .await?;
    
    Ok(())
}
```
## Features
### Zero Boilerplate State Definition
```rust
#[state]
pub enum ProcessState {
    Ready,
    Working,
    Complete,
}
```
### Automatic Constructor Generation
The `#[context]` attribute automatically generates an async `new` constructor and handles the PhantomData marker:
```rust
#[context]
struct ApiClient<S: ProcessState> {
    client: reqwest::Client,
    base_url: String,
}

// Generated automatically:
impl<S: ProcessState> ApiClient<S> {
    fn new(client: reqwest::Client, base_url: String) -> Self {
        Self {
            client,
            base_url,
            marker: PhantomData,
        }
    }
}
```
### Clean State Transitions
Transition between states using `.into_context()`:
```rust
impl ApiClient<Ready> {
    async fn connect(self) -> Result<ApiClient<Working>> {
        // Just focus on the logic
        Ok(self.into_context())  // Explicit state transition
    }
}
```
### Rich Context
Your state machine can maintain any context it needs:
```rust
#[context]
struct RichContext<S: ProcessState> {
    id: Uuid,
    created_at: DateTime<Utc>,
    metadata: HashMap<String, String>,
    config: Config,
}
```
## Real World Example
Here's a more complete example showing async operations and state transitions:
```rust
use statum::{state, context};
use anyhow::Result;

#[state]
pub enum PublishState {
    Draft,
    Review,
    Published,
    Archived,
}

#[context]
struct Article<S: PublishState> {
    id: Uuid,
    content: String,
    client: ApiClient,
}

impl Article<Draft> {
    async fn submit_for_review(self) -> Result<Article<Review>> {
        self.client.save_draft(&self.id, &self.content).await?;
        Ok(self.into_context())
    }
}

impl Article<Review> {
    async fn approve(self) -> Result<Article<Published>> {
        self.client.publish(&self.id).await?;
        Ok(self.into_context())
    }
    
    async fn request_changes(self) -> Result<Article<Draft>> {
        self.client.reject(&self.id).await?;
        Ok(self.into_context())
    }
}

impl Article<Published> {
    async fn archive(self) -> Result<Article<Archived>> {
        self.client.archive(&self.id).await?;
        Ok(self.into_context())
    }
}

#[tokio::main]
async fn main() -> Result<()> {
    let article = Article::new(
        Uuid::new_v4(),
        "My Article".to_string(),
        ApiClient::new().await,
    );
    let published = article
        .submit_for_review().await?
        .approve().await?;
        
    Ok(())
}
```
## Contributing
Contributions welcome! Feel free to submit pull requests.
## License
MIT License - see LICENSE for details.