http-provider-macro 0.1.0

A procedural macro for generating type-safe HTTP client providers
Documentation
# HTTP Provider Macro

A Rust procedural macro that generates HTTP client providers with compile-time endpoint definitions. This macro eliminates boilerplate code for creating HTTP clients by automatically generating methods for your API endpoints.

## Features

- 🚀 **Zero runtime overhead** - All HTTP client code is generated at compile time
- 🔧 **Automatic method generation** - Function names auto-generated from HTTP method and path
- 🎯 **Type-safe requests/responses** - Full Rust type checking for all parameters
- 🌐 **Full HTTP method support** - GET, POST, PUT, DELETE
- 📝 **Path parameters** - Dynamic URL path substitution with `{param}` syntax
- 🔍 **Query parameters** - Automatic query string serialization
- 📋 **Custom headers** - Per-request header support
-**Async/await** - Built on reqwest with full async support
- ⏱️ **Configurable timeouts** - Per-client timeout configuration

## Quick Start

Add this to your `Cargo.toml`:

```toml
[dependencies]
http-provider-macro = "0.1.0"
reqwest = { version = "0.11", features = ["json"] }
serde = { version = "1.0", features = ["derive"] }
tokio = { version = "1.0", features = ["full"] }
```

## Basic Usage

```rust
use http_provider_macro::http_provider;
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize)]
struct User {
    id: u32,
    name: String,
    email: String,
}

#[derive(Serialize)]
struct CreateUserRequest {
    name: String,
    email: String,
}

// Define your HTTP provider
http_provider!(
    UserApiProvider,
    {
        {
            path: "/users",
            method: GET,
            res: Vec<User>,
        },
        {
            path: "/users",
            method: POST,
            req: CreateUserRequest,
            res: User,
        },
        {
            path: "/users/{id}",
            method: GET,
            path_params: PathParams,
            res: User,
        }
    }
);

#[derive(Serialize)]
struct PathParams {
    id: u32,
}

#[tokio::main]
async fn main() -> Result<(), Box<dyn std::error::Error>> {
    let base_url = reqwest::Url::parse("https://api.example.com")?;
    let client = UserApiProvider::new(base_url, 30); // 30 second timeout

    // GET /users - auto-generated method name: get_users
    let users = client.get_users().await?;
    println!("Users: {:?}", users);

    // POST /users - auto-generated method name: post_users  
    let new_user = client.post_users(&CreateUserRequest {
        name: "John Doe".to_string(),
        email: "john@example.com".to_string(),
    }).await?;
    println!("Created user: {:?}", new_user);

    // GET /users/{id} - auto-generated method name: get_users_id
    let user = client.get_users_id(&PathParams { id: 1 }).await?;
    println!("User: {:?}", user);

    Ok(())
}
```

## Endpoint Configuration

Each endpoint is defined within braces `{}` with the following fields:

### Required Fields

- **`path`**: The API endpoint path (string literal)
- **`method`**: HTTP method (`GET`, `POST`, `PUT`, `DELETE`)  
- **`res`**: Response type that implements `Deserialize`

### Optional Fields

- **`fn_name`**: Custom function name (defaults to auto-generated)
- **`req`**: Request body type that implements `Serialize`
- **`headers`**: Header type (typically `reqwest::header::HeaderMap`)
- **`query_params`**: Query parameters type that implements `Serialize`
- **`path_params`**: Path parameters type with fields matching `{param}` in path

## Advanced Examples

### Custom Function Names and Headers

```rust
use reqwest::header::HeaderMap;

http_provider!(
    ApiProvider,
    {
        {
            path: "/protected/data",
            method: GET,
            fn_name: fetch_protected_data,
            res: ApiResponse,
            headers: HeaderMap,
        }
    }
);

// Usage
let mut headers = HeaderMap::new();
headers.insert("Authorization", "Bearer token123".parse()?);
let data = client.fetch_protected_data(headers).await?;
```

### Query Parameters

```rust
#[derive(Serialize)]
struct SearchQuery {
    q: String,
    limit: u32,
    offset: u32,
}

http_provider!(
    SearchProvider,
    {
        {
            path: "/search",
            method: GET,
            query_params: SearchQuery,
            res: SearchResults,
        }
    }
);

// Usage
let results = client.get_search(&SearchQuery {
    q: "rust".to_string(),
    limit: 10,
    offset: 0,
}).await?;
```

### Complex Path Parameters

```rust
#[derive(Serialize)]
struct ResourcePath {
    user_id: u32,
    resource_id: String,
}

http_provider!(
    ResourceProvider,
    {
        {
            path: "/users/{user_id}/resources/{resource_id}",
            method: GET,
            path_params: ResourcePath,
            res: Resource,
        }
    }
);

// Usage  
let resource = client.get_users_user_id_resources_resource_id(&ResourcePath {
    user_id: 123,
    resource_id: "abc-def".to_string(),
}).await?;
```

### All Parameters Combined

```rust
http_provider!(
    CompleteProvider,
    {
        {
            path: "/api/v1/users/{user_id}/posts",
            method: POST,
            fn_name: create_user_post,
            path_params: UserPath,
            req: CreatePostRequest,
            res: Post,
            headers: HeaderMap,
            query_params: PostQuery,
        }
    }
);

// Usage
let post = client.create_user_post(
    &UserPath { user_id: 123 },
    &CreatePostRequest { title: "Hello".to_string() },
    headers,
    &PostQuery { draft: false },
).await?;
```

## Generated Code Structure

The macro generates:

1. **Struct Definition**: A provider struct with `url`, `client`, and `timeout` fields
2. **Constructor**: `new(url: reqwest::Url, timeout: u64) -> Self`
3. **HTTP Methods**: One async method per endpoint definition

### Method Signatures

Generated methods follow this pattern:

```rust
pub async fn method_name(
    &self,
    path_params: &PathParamsType,    // if path_params specified
    body: &RequestType,              // if req specified  
    headers: HeaderMap,              // if headers specified
    query: &QueryType,               // if query_params specified
) -> Result<ResponseType, String>
```

### Auto-generated Function Names

When `fn_name` is not specified, names are generated as:
- `{method}_{path}` where path slashes become underscores
- Examples:
  - `GET /users``get_users`
  - `POST /api/v1/posts``post_api_v1_posts`
  - `PUT /users/{id}``put_users_id`

## Error Handling

All generated methods return `Result<T, String>` where errors include:

- **URL construction errors**: Invalid path parameter substitution
- **Network errors**: Connection timeouts, DNS failures, etc.
- **HTTP errors**: Non-2xx status codes with status information
- **Deserialization errors**: JSON parsing failures

## Requirements

- **Rust 1.70+**: For latest async/await and procedural macro features
- **reqwest**: HTTP client library
- **serde**: Serialization framework
- **tokio**: Async runtime

## License

Licensed under either of

 * Apache License, Version 2.0, ([LICENSE-APACHE]LICENSE-APACHE or http://www.apache.org/licenses/LICENSE-2.0)
 * MIT license ([LICENSE-MIT]LICENSE-MIT or http://opensource.org/licenses/MIT)

at your option.

## Contributing

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