archy 0.3.1

An async application framework with services, systems, and dependency injection
Documentation
# archy

A lightweight async application framework for Rust. Inspired by Bevy/Axum, built for async.

## Installation

```toml
[dependencies]
archy = "0.3"
tokio = { version = "1", features = ["full"] }
```

## Quick Start

```rust
use archy::{App, Schedule, Shutdown};

async fn hello(shutdown: Shutdown) {
    println!("Hello, World!");
    shutdown.trigger();
}

#[tokio::main]
async fn main() {
    let mut app = App::new();
    app.add_system(Schedule::Run, hello);
    app.run().await;
}
```

## Services

Services are actor-like components that process messages. The `#[derive(Service)]` and `#[service]` macros generate message types and client methods:

```rust
use archy::prelude::*;

#[derive(Service)]
struct Greeter {}

#[service]
impl Greeter {
    pub async fn greet(&self, name: String) -> String {
        format!("Hello, {name}!")
    }
}
```

Register services and call them using `Client<S>`:

```rust
app.add_service::<Greeter>();
```

```rust
async fn greet_user(greeter: Client<Greeter>, shutdown: Shutdown) {
    let message = greeter.greet("World".into()).await.unwrap();
    println!("{message}");
    shutdown.trigger();
}
```

Service calls return `Result<T, ServiceError>`. Use `.timeout()` to add timeouts:

```rust
use std::time::Duration;

let result = greeter.greet("World".into()).timeout(Duration::from_secs(5)).await;
```

## Systems

Systems are async functions that run at specific lifecycle phases:

- `Schedule::First` - Before services start
- `Schedule::Startup` - After services start
- `Schedule::Run` - Background tasks until shutdown
- `Schedule::Fixed(Duration)` - Periodic interval tasks
- `Schedule::Shutdown` - When shutdown triggered
- `Schedule::Last` - After everything stops

```rust
use std::time::Duration;

app.add_system(Schedule::First, run_migrations);
app.add_system(Schedule::Startup, warmup_cache);
app.add_system(Schedule::Fixed(Duration::from_secs(30)), health_check);
```

Systems can return `Result<(), E>` to trigger restart policies on error.

## Resources

Shared state injected into services and systems:

```rust
struct Config { db_url: String }

app.add_resource(Config { db_url: "postgres://...".into() });

async fn my_system(config: Res<Config>) {
    println!("DB: {}", config.db_url);
}

// Services use the same extractors
#[derive(Service)]
struct OrderService {
    db: Res<Database>,
    payments: Client<PaymentService>,
}
```

## Events

Pub/sub broadcasting with `Emit<E>` and `Sub<E>`:

```rust
#[derive(Clone)]
struct UserCreated { id: u64 }

app.add_event::<UserCreated>();

async fn create_user(emit: Emit<UserCreated>) {
    emit.emit(UserCreated { id: 42 });
}

async fn log_users(mut events: Sub<UserCreated>) {
    while let Some(event) = events.recv().await {
        println!("User created: {}", event.id);
    }
}
```

## Configuration

Services, systems, and events can be configured:

```rust
// Service config
app.add_service::<MyService>()
    .workers(4)
    .capacity(64)
    .concurrent(10)  // or .sequential()
    .restart(RestartPolicy::Always);

// System config
app.add_system(Schedule::Run, my_system)
    .workers(2)
    .restart(RestartPolicy::attempts(3));

// Event config
app.add_event::<MyEvent>()
    .capacity(128);

// Shutdown timeout
app.shutdown().timeout(Duration::from_secs(30));
```

Set defaults before registration:

```rust
app.service_defaults().workers(2).capacity(64);
app.system_defaults().restart(RestartPolicy::Always);
app.event_defaults().capacity(128);
```

## Restart Policies

Control restart behavior for services and systems:

```rust
RestartPolicy::Never              // No restart (default)
RestartPolicy::Always             // Always restart on panic/error
RestartPolicy::Attempts { max: 3, reset_after: Some(Duration::from_secs(60)) }
RestartPolicy::attempts(3)        // Helper with default reset window
```

Services restart on panic. Systems restart on panic or `Err` return.

## Batch Registration

Register multiple items using tuples:

```rust
app.add_resources((config, db_pool, cache));
app.add_services::<(ServiceA, ServiceB, ServiceC)>();
app.add_events::<(Event1, Event2)>();
app.add_systems(Schedule::Run, (system_a, system_b, system_c));
```

## Modules

Organize registration with the `Module` trait:

```rust
struct PaymentModule { config: PaymentConfig }

impl Module for PaymentModule {
    fn register(self, app: &mut App) {
        app.add_resource(self.config);
        app.add_service::<PaymentService>();
        app.add_event::<PaymentProcessed>();
    }
}

app.add_module(PaymentModule { config });
```

## Error Handling

Service calls return `Result<T, ServiceError>`:

```rust
pub enum ServiceError {
    ChannelClosed,   // Service shutting down
    ServiceDropped,  // Service panicked
    Timeout,         // From .timeout()
}
```

Flatten nested results with `.flatten_into()`:

```rust
// When a service method returns Result<T, BusinessError>
let result: Result<User, AppError> = users.create(data).flatten_into().await;
// Requires: AppError: From<ServiceError> + From<BusinessError>
```

## License

Licensed under either of [Apache License, Version 2.0](LICENSE-APACHE) or [MIT license](LICENSE-MIT).