diff --git a/.gitignore b/.gitignore index 06fdf26..1c7e1db 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,6 @@ /target -docs/ \ No newline at end of file +docs/ + +# macOS +.DS_Store \ No newline at end of file diff --git a/CHANGELOG.md b/CHANGELOG.md index 3feb060..7fccd69 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,23 @@ All notable changes to this project are documented here. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [0.3.1] + +Documentation and metadata only — no code or behavior changes. + +### Documentation + +- The crate-level docs (docs.rs) are now sourced from the README via + `#![doc = include_str!("../README.md")]`, so docs.rs and crates.io no longer drift — + auth, retries, `with_client`, and error handling are all documented in both places. +- README: documented the method-naming rule, and example links are now absolute so they + resolve on crates.io and docs.rs. + +### Changed + +- Declared the minimum supported Rust version: `rust-version = "1.75"` (the generated + mocking trait uses `async fn` in traits). This was already required; it is now explicit. + ## [0.3.0] This release contains breaking changes to the constructor signature and the generated diff --git a/Cargo.lock b/Cargo.lock index 5961e6c..c0dd27c 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -41,7 +41,7 @@ checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "beckon" -version = "0.3.0" +version = "0.3.1" dependencies = [ "heck", "proc-macro2", diff --git a/Cargo.toml b/Cargo.toml index 1199d5a..e5834b7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,8 @@ [package] name = "beckon" -version = "0.3.0" +version = "0.3.1" edition = "2021" +rust-version = "1.75" authors = ["Azeem Shaik "] description = "Generate type-safe, async HTTP clients from endpoint definitions — a Rust proc macro." repository = "https://github.com/azeemshaik025/beckon" diff --git a/README.md b/README.md index 892aff6..b632175 100644 --- a/README.md +++ b/README.md @@ -25,22 +25,24 @@ cargo add serde --features derive cargo add tokio --features full ``` +Requires **Rust 1.75+** — the generated mocking trait uses `async fn` in traits. + ## Example -```rust +```rust,no_run use beckon::beckon; use serde::{Deserialize, Serialize}; use std::time::Duration; #[derive(Serialize, Deserialize)] -struct User { - id: u32, - name: String, +pub struct User { + pub id: u32, + pub name: String, } #[derive(Serialize)] -struct UserPath { - id: u32, +pub struct UserPath { + pub id: u32, } beckon!( @@ -97,11 +99,14 @@ async fn main() -> Result<(), Box> { - `fn_name`: Custom method name - `retries`: Retry count for this endpoint (overrides the global setting) +Method names are derived from the method and path — `GET /users` → `get_users`, +`GET /users/{id}` → `get_users_by_id`. Set `fn_name` to override. + ## Auth Add automatic authentication to every request. Three strategies are supported: -```rust +```rust,ignore // Bearer token — injects `Authorization: Bearer ` beckon!(GithubApi, auth: Bearer, { /* ... */ }); let client = GithubApi::new(url, "ghp_xxxx", Duration::from_secs(5)); @@ -124,7 +129,7 @@ Set a global retry count that applies to all endpoints. Retries use exponential (100ms base, 2x multiplier, 5s cap) and trigger on 5xx errors and request timeouts. 4xx errors are never retried. -```rust +```rust,ignore beckon!( UserApi, retries: 3, @@ -154,7 +159,7 @@ Per-endpoint `retries` overrides the global value. Omitting `retries` entirely m `timeout` accepts a `std::time::Duration`, or `None` for the 5-second default. -```rust +```rust,ignore let http = reqwest::Client::builder().user_agent("my-app/1.0").build()?; let client = UserApi::with_client(url, http, Duration::from_secs(5)); ``` @@ -165,7 +170,7 @@ Every method returns `Result`. On a non-2xx response you get `Http` variant, which carries the server's response body so you can see *why* a request was rejected: -```rust +```rust,ignore match client.get_users_by_id(&UserPath { id: 999 }).await { Ok(user) => { /* ... */ } Err(UserApiError::Http { status, reason, body }) => { @@ -188,14 +193,14 @@ For a client named `UserApi`, the macro generates: ## Examples -See the [`examples/`](examples/) directory: +See the [`examples/`](https://github.com/azeemshaik025/beckon/tree/main/examples) directory: -- [`basic.rs`](examples/basic.rs) — simple GET requests -- [`params.rs`](examples/params.rs) — path and query parameters -- [`advanced.rs`](examples/advanced.rs) — all features -- [`mocking.rs`](examples/mocking.rs) — testing with the generated trait -- [`multiple_path_params.rs`](examples/multiple_path_params.rs) — nested resources -- [`error_handling.rs`](examples/error_handling.rs) — inspecting a failed request's body +- [`basic.rs`](https://github.com/azeemshaik025/beckon/blob/main/examples/basic.rs) — simple GET requests +- [`params.rs`](https://github.com/azeemshaik025/beckon/blob/main/examples/params.rs) — path and query parameters +- [`advanced.rs`](https://github.com/azeemshaik025/beckon/blob/main/examples/advanced.rs) — all features +- [`mocking.rs`](https://github.com/azeemshaik025/beckon/blob/main/examples/mocking.rs) — testing with the generated trait +- [`multiple_path_params.rs`](https://github.com/azeemshaik025/beckon/blob/main/examples/multiple_path_params.rs) — nested resources +- [`error_handling.rs`](https://github.com/azeemshaik025/beckon/blob/main/examples/error_handling.rs) — inspecting a failed request's body ## License diff --git a/src/lib.rs b/src/lib.rs index b5b43cb..ef94292 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,50 +1,4 @@ -//! Generate type-safe, async HTTP clients from endpoint definitions. -//! -//! `beckon!` takes a client name and a list of endpoints and expands to a struct -//! with one async method per endpoint, a matching trait for mocking, and a typed -//! error enum. -//! -//! ``` -//! use beckon::beckon; -//! use serde::{Deserialize, Serialize}; -//! use std::time::Duration; -//! -//! #[derive(Serialize, Deserialize)] -//! pub struct User { -//! pub id: u32, -//! pub name: String, -//! } -//! -//! #[derive(Serialize)] -//! pub struct UserPath { -//! pub id: u32, -//! } -//! -//! beckon!( -//! UserApi, -//! { -//! { -//! path: "/users", -//! method: GET, -//! res: Vec, -//! }, -//! { -//! path: "/users/{id}", -//! method: GET, -//! path_params: UserPath, -//! res: User, -//! } -//! } -//! ); -//! -//! # async fn example() -> Result<(), Box> { -//! let client = UserApi::new(reqwest::Url::parse("https://api.example.com")?, Duration::from_secs(5)); -//! let _users = client.get_users().await?; -//! let _user = client.get_users_by_id(&UserPath { id: 1 }).await?; -//! # Ok(()) -//! # } -//! # fn main() {} -//! ``` +#![doc = include_str!("../README.md")] extern crate proc_macro;