dynami 0.1.0

Automatic Axum router generation from directory structure with file-system based routing
Documentation
# Dynami


A Rust library for automatic Axum router generation from directory structure. Generate type-safe routers for Axum 0.8+ by organizing your route handlers into a folder hierarchy.

## Features


- **File-system based routing** - Routes are automatically generated from your folder structure
- **Soft overwriting** - Preserves your custom code outside of generated sections
- **Smart route detection** - Won't overwrite routes you've manually defined
- **Dynamic routes** - Use `d_` prefix for path parameters (e.g., `d_id` -> `/{id}`)
- **Wildcard routes** - Support for multi-segment captures (e.g., `d_*rest` -> `/{*rest}`)
- **State detection** - Automatically detects and applies your AppState type
- **All HTTP methods** - GET, POST, PUT, DELETE, PATCH, OPTIONS, HEAD
- **Auto-generated handlers** - Creates default handlers for empty method files

## Installation


Add to your `Cargo.toml`:

```toml
[dependencies]
dynami = "0.1"

[build-dependencies]
dynami = "0.1"
```

## Usage


### 1. Create a folder structure for your routes


```
src/routes/
├── mod.rs           # Root router (optional AppState here)
├── get.rs           # GET /
├── post.rs          # POST /
└── api/
    ├── mod.rs       # API router
    ├── get.rs       # GET /api
    └── d_id/
        ├── mod.rs   # Dynamic router
        └── get.rs   # GET /api/{id}
```

### 2. Create a build.rs file


```rust
use dynami::format_routes;

fn main() {
    format_routes("./src/routes").unwrap();
    println!("cargo:rerun-if-changed=src/routes");
}
```

### 3. Run your build


```bash
cargo build
```

The library will generate:
- `router()` functions in each `mod.rs`
- `pub mod` declarations for subdirectories
- Default handlers for empty method files
- Proper type annotations with your AppState

### 4. Use the generated router in your application


```rust
mod routes;

#[tokio::main]

async fn main() {
    let app = routes::router();
    
    let listener = tokio::net::TcpListener::bind("0.0.0.0:3000")
        .await
        .unwrap();
    
    axum::serve(listener, app).await.unwrap();
}
```

## How it Works


### Generated Code with `dynami::generate!`


The library uses the `dynami::generate!` macro to mark generated code blocks:

```rust
use axum::Router;
use axum::routing::get;

pub fn router() -> Router {
    let mut router = Router::new();
    
    // You can add custom routes here - they won't be overwritten
    router = router.route("/custom", get(custom_handler));
    
    dynami::generate! {
        // This block is auto-generated - don't edit manually
        router = router.route("/", get(get::handler));
        router = router.nest("/api", api::router());
    }
    
    router
}
```

**Key benefits:**
- Add custom routes before or after the generated block
- The generator detects your manual routes and won't duplicate them
- Smart import generation: only imports the HTTP methods actually used (e.g., `use axum::routing::{get, post}` only if both are used)
- If you remove `dynami::generate!`, the file won't be updated anymore
- The macro is a simple pass-through that returns its input, so the code compiles normally

### HTTP Method Files


Create files named after HTTP methods:
- `get.rs` -> GET handler
- `post.rs` -> POST handler
- `put.rs` -> PUT handler
- `delete.rs` -> DELETE handler
- `patch.rs` -> PATCH handler
- `options.rs` -> OPTIONS handler
- `head.rs` -> HEAD handler

### Dynamic Routes


Use the `d_` prefix for path parameters:

- `d_id/` -> `/{id}` route
- `d_user_id/` -> `/{user_id}` route
- `d_*rest/` -> `/{*rest}` wildcard route (captures remaining path)

### AppState Detection


Add your state struct to the root `mod.rs`:

```rust
#[derive(Clone)]

pub struct AppState {
    pub db: Database,
}

pub fn router() -> Router<AppState> {
    let mut router = Router::new();
    dynami::generate! {
        router = router.route("/", get(get::handler));
    }
    router
}
```

The library will:
1. Detect the `AppState` struct (or any struct with "State" in its name)
2. Generate `Router<AppState>` for all routers
3. Include `State` extractors in default handlers

## Examples


### Simple API


```
routes/
├── mod.rs
├── get.rs        # GET / - list all
└── post.rs       # POST / - create new
```

Generates:
```rust
use axum::Router;
use axum::routing::{get, post};

pub fn router() -> Router {
    let mut router = Router::new();
    dynami::generate! {
        router = router.route("/", get(get::handler).post(post::handler));
    }
    router
}
```

### Nested Routes


```
routes/
├── mod.rs
├── get.rs
└── api/
    ├── mod.rs
    ├── get.rs
    └── users/
        ├── mod.rs
        └── get.rs
```

Generates in `routes/mod.rs`:
```rust
pub mod api;

use axum::Router;
use axum::routing::get;

pub fn router() -> Router {
    let mut router = Router::new();
    dynami::generate! {
        router = router.route("/", get(get::handler));
        router = router.nest("/api", api::router());
    }
    router
}
```

### Mixing Manual and Generated Routes


```rust
// routes/mod.rs
use axum::Router;
use axum::routing::get;

pub mod api;

// Custom handler defined locally
async fn health_check() -> &'static str {
    "OK"
}

pub fn router() -> Router {
    let mut router = Router::new();
    
    // Manual route - generator will detect this and skip generating GET /
    router = router.route("/health", get(health_check));
    
    dynami::generate! {
        // Only routes not already defined above will be generated
        router = router.nest("/api", api::router());
    }
    
    router
}
```

### With AppState


```rust
// routes/mod.rs
use sqlx::PgPool;

#[derive(Clone)]

pub struct AppState {
    pub pool: PgPool,
}

pub fn router() -> Router<AppState> {
    let mut router = Router::new();
    dynami::generate! {
        router = router.route("/", get(get::handler));
    }
    router
}
```

```rust
// routes/get.rs
use axum::extract::State;
use axum::response::IntoResponse;
use super::AppState;

pub async fn handler(State(state): State<AppState>) -> impl IntoResponse {
    // Use state.pool here
    "Hello World"
}
```

## Default Handlers


Empty method files get default handlers:

```rust
// Without state
use axum::response::IntoResponse;

pub async fn handler() -> impl IntoResponse {
    "OK"
}
```

```rust
// With state (when AppState is detected)
use axum::extract::State;
use axum::response::IntoResponse;

pub async fn handler(State(state): State<AppState>) -> impl IntoResponse {
    "OK"
}
```

## Best Practices


1. **Run in build.rs** - Generate routes during build time
2. **Keep the `dynami::generate!` block** - If you remove it, the file won't be updated
3. **Add custom code outside the generate block** - Your code won't be overwritten
4. **Use meaningful folder names** - They become route paths
5. **Implement handlers** - Replace default "OK" responses with real logic
6. **Define custom routes before the generate block** - They'll be detected and not duplicated

## Limitations


- Axum doesn't support regex in path parameters (use middleware for validation)
- Route generation happens at build time (requires rebuild for new routes)
- Wildcard routes must use `d_*` prefix (e.g., `d_*rest`)

## License


This project is open source and available under the MIT License.

## Contributing


Contributions are welcome! Please feel free to submit a Pull Request.