Struct ApiCall

Source
pub struct ApiCall { /* private fields */ }
Expand description

Builder for configuring HTTP API calls with comprehensive parameter and validation support.

ApiCall provides a fluent interface for building HTTP requests with automatic OpenAPI schema collection. It supports query parameters, headers, request bodies, and flexible status code validation.

§Method Groups

§Request Body Methods

§Parameter Methods

§Status Code Validation

§OpenAPI Metadata

§Response Descriptions

§Execution

  • .await - Execute the request and return response (⚠️ must consume result for OpenAPI)

§Default Behavior

  • Status codes: Accepts 200-499 (inclusive of 200, exclusive of 500)
  • Content-Type: Automatically set based on body type
  • Schema collection: Request/response schemas are automatically captured
  • Operation metadata: Automatically generated if not explicitly set

§Automatic OpenAPI Metadata Generation

When you don’t explicitly set operation metadata, ApiCall automatically generates:

§Automatic Tags

Tags are extracted from the request path using intelligent parsing:

Path: /api/v1/users/{id}     → Tags: ["users"]
Path: /users                 → Tags: ["users"]
Path: /users/export          → Tags: ["users", "export"]
Path: /observations/import   → Tags: ["observations", "import"]

Path Prefix Skipping: Common API prefixes are automatically skipped:

  • api, v1, v2, v3, rest, service (and more)
  • /api/v1/users becomes ["users"], not ["api", "v1", "users"]

Special Action Detection: Certain path segments get their own tags:

  • import, upload, export, search, bulk
  • /users/export["users", "export"]

§Automatic Descriptions

Descriptions are generated based on HTTP method and path:

GET /users          → "Retrieve users"
GET /users/{id}     → "Retrieve user by ID"  
POST /users         → "Create user"
PUT /users/{id}     → "Update user by ID"
DELETE /users/{id}  → "Delete user by ID"

§Automatic Operation IDs

Generated from HTTP method and path: "get-users-id", "post-users", etc.

You can override any of these by calling the corresponding with_* methods.

Implementations§

Source§

impl ApiCall

Source

pub fn with_operation_id(self, operation_id: impl Into<String>) -> Self

Source

pub fn with_description(self, description: impl Into<String>) -> Self

Sets the operation description for OpenAPI documentation.

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.get("/users")?.with_description("Retrieve all users");
Source

pub fn with_tags<I, T>(self, tags: I) -> Self
where I: IntoIterator<Item = T>, T: Into<String>,

Sets the operation tags for OpenAPI categorization.

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.get("/users")?.with_tags(vec!["users", "admin"]);
// Also works with arrays, slices, or any IntoIterator
let call = client.get("/users")?.with_tags(["users", "admin"]);
Source

pub fn with_tag(self, tag: impl Into<String>) -> Self

Adds a single tag to the operation for OpenAPI categorization.

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.get("/users")?.with_tag("users").with_tag("admin");
Source

pub fn with_response_description(self, description: impl Into<String>) -> Self

Sets a response description for the actual returned status code.

This method allows you to document what the response means for your API endpoint. The description will be applied to whatever status code is actually returned by the server and included in the generated OpenAPI specification.

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.get("/users/{id}")?
    .with_response_description("User details if found, or error information");
Source

pub fn without_collection(self) -> Self

Excludes this API call from OpenAPI collection and documentation generation.

When called, this API call will be executed normally but will not appear in the generated OpenAPI specification. This is useful for:

  • Health check endpoints
  • Debug/diagnostic endpoints
  • Authentication/session management calls
  • Test setup/teardown calls
  • Internal utility endpoints
  • Administrative endpoints not part of public API
§Examples
let mut client = ApiClient::builder().build()?;

// Health check that won't appear in OpenAPI spec
client
    .get("/health")?
    .without_collection()
    .await?
    .as_empty()
    .await?;

// Debug endpoint excluded from documentation
client
    .get("/debug/status")?
    .without_collection()
    .await?
    .as_text()
    .await?;
Source

pub fn with_query(self, query: CallQuery) -> Self

Source

pub fn with_headers_option(self, headers: Option<CallHeaders>) -> Self

Source

pub fn with_headers(self, headers: CallHeaders) -> Self

Adds headers to the API call, merging with any existing headers.

This is a convenience method that automatically wraps the headers in Some().

Source

pub fn with_header<T: ParameterValue>( self, name: impl Into<String>, value: impl Into<ParamValue<T>>, ) -> Self

Convenience method to add a single header.

This method automatically handles type conversion and merges with existing headers. If a header with the same name already exists, the new value will override it.

§Examples
§Basic Usage
let mut client = ApiClient::builder().build()?;
let call = client.get("/users")?
    .with_header("Authorization", "Bearer token123")
    .with_header("X-Request-ID", "abc-123-def");
§Type Flexibility and Edge Cases
let mut client = ApiClient::builder().build()?;

// Different value types are automatically converted
let call = client.post("/api/data")?
    .with_header("Content-Length", 1024_u64)           // Numeric values
    .with_header("X-Retry-Count", 3_u32)               // Different numeric types
    .with_header("X-Debug", true)                      // Boolean values
    .with_header("X-Session-ID", "session-123");       // String values

// Headers can be chained and overridden
let call = client.get("/protected")?
    .with_header("Authorization", "Bearer old-token")
    .with_header("Authorization", "Bearer new-token");  // Overrides previous value
Source

pub fn with_cookies(self, cookies: CallCookies) -> Self

Adds cookies to the API call, merging with any existing cookies.

This method accepts a CallCookies instance and merges it with any existing cookies on the request. Cookies are sent in the HTTP Cookie header and can be used for session management, authentication, and storing user preferences.

§Examples
let mut client = ApiClient::builder().build()?;
let cookies = CallCookies::new()
    .add_cookie("session_id", "abc123")
    .add_cookie("user_id", 456);

let call = client.get("/dashboard")?
    .with_cookies(cookies);

Convenience method to add a single cookie.

This method automatically handles type conversion and merges with existing cookies. If a cookie with the same name already exists, the new value will override it.

§Examples
§Basic Usage
let mut client = ApiClient::builder().build()?;
let call = client.get("/dashboard")?
    .with_cookie("session_id", "abc123")
    .with_cookie("user_id", 456);
§Type Flexibility and Edge Cases
let mut client = ApiClient::builder().build()?;

// Different value types are automatically converted
let call = client.get("/preferences")?
    .with_cookie("theme", "dark")                    // String values
    .with_cookie("user_id", 12345_u64)              // Numeric values
    .with_cookie("is_premium", true)                // Boolean values
    .with_cookie("selected_tags", vec!["rust", "web"]); // Array values

// Cookies can be chained and overridden
let call = client.get("/profile")?
    .with_cookie("session_id", "old-session")
    .with_cookie("session_id", "new-session");      // Overrides previous value
Source

pub fn with_authentication(self, authentication: Authentication) -> Self

Overrides the authentication for this specific request.

This method allows you to use different authentication for a specific request, overriding the default authentication configured on the API client.

§Examples
use clawspec_core::{ApiClient, Authentication};

// Client with default authentication
let mut client = ApiClient::builder()
    .with_authentication(Authentication::Bearer("default-token".into()))
    .build()?;

// Use different authentication for a specific request
let response = client
    .get("/admin/users")?
    .with_authentication(Authentication::Bearer("admin-token".into()))
    .await?;

// Remove authentication for a public endpoint
let response = client
    .get("/public/health")?
    .with_authentication_none()
    .await?;
Source

pub fn with_authentication_none(self) -> Self

Removes authentication for this specific request.

This is useful when making requests to public endpoints that don’t require authentication, even when the client has default authentication configured.

§Examples
use clawspec_core::{ApiClient, Authentication};

// Client with default authentication
let mut client = ApiClient::builder()
    .with_authentication(Authentication::Bearer("token".into()))
    .build()?;

// Remove authentication for public endpoint
let response = client
    .get("/public/status")?
    .with_authentication_none()
    .await?;
Source

pub fn with_status_range_inclusive(self, range: RangeInclusive<u16>) -> Self

Sets the expected status codes for this request using an inclusive range.

By default, status codes 200..500 are considered successful. Use this method to customize which status codes should be accepted.

§Examples
§Basic Usage
let mut client = ApiClient::builder().build()?;

// Accept only 200 to 201 (inclusive)
let call = client.post("/users")?.with_status_range_inclusive(200..=201);

// Accept any 2xx status code
let call = client.get("/users")?.with_status_range_inclusive(200..=299);
§Edge Cases
let mut client = ApiClient::builder().build()?;

// Single status code range (equivalent to with_expected_status)
let call = client.get("/health")?.with_status_range_inclusive(200..=200);

// Accept both success and client error ranges  
let call = client.delete("/users/123")?
    .with_status_range_inclusive(200..=299)
    .add_expected_status_range_inclusive(400..=404);

// Handle APIs that return 2xx or 3xx for different success states
let call = client.post("/async-operation")?.with_status_range_inclusive(200..=302);
Source

pub fn with_status_range(self, range: Range<u16>) -> Self

Sets the expected status codes for this request using an exclusive range.

§Examples
let mut client = ApiClient::builder().build()?;

// Accept 200 to 299 (200 included, 300 excluded)
let call = client.get("/users")?.with_status_range(200..300);
Source

pub fn with_expected_status(self, status: u16) -> Self

Sets a single expected status code for this request.

§Examples
let mut client = ApiClient::builder().build()?;

// Accept only 204 for DELETE operations
let call = client.delete("/users/123")?.with_expected_status(204);
Source

pub fn add_expected_status(self, status: u16) -> Self

Adds an additional expected status code to the existing set.

§Examples
let mut client = ApiClient::builder().build()?;

// Accept 200..299 and also 404
let call = client.get("/users")?.with_status_range_inclusive(200..=299).add_expected_status(404);
Source

pub fn add_expected_status_range_inclusive( self, range: RangeInclusive<u16>, ) -> Self

Adds an additional expected status range (inclusive) to the existing set.

§Examples
let mut client = ApiClient::builder().build()?;

// Accept 200..=204 and also 400..=402
let call = client.post("/users")?.with_status_range_inclusive(200..=204).add_expected_status_range_inclusive(400..=402);
Source

pub fn add_expected_status_range(self, range: Range<u16>) -> Self

Adds an additional expected status range (exclusive) to the existing set.

§Examples
let mut client = ApiClient::builder().build()?;

// Accept 200..=204 and also 400..403
let call = client.post("/users")?.with_status_range_inclusive(200..=204).add_expected_status_range(400..403);
Source

pub fn with_success_only(self) -> Self

Convenience method to accept only 2xx status codes (200..300).

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.get("/users")?.with_success_only();
Source

pub fn with_client_errors(self) -> Self

Convenience method to accept 2xx and 4xx status codes (200..500, excluding 3xx).

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.post("/users")?.with_client_errors();
Source

pub fn with_expected_status_codes(self, codes: ExpectedStatusCodes) -> Self

Sets the expected status codes using an ExpectedStatusCodes instance.

This method allows you to pass pre-configured ExpectedStatusCodes instances, which is particularly useful with the expected_status_codes! macro.

§Examples
use clawspec_core::{ApiClient, expected_status_codes};

let mut client = ApiClient::builder().build()?;

// Using the macro with with_expected_status_codes
let call = client.get("/users")?
    .with_expected_status_codes(expected_status_codes!(200-299));

// Using manually created ExpectedStatusCodes
let codes = clawspec_core::ExpectedStatusCodes::from_inclusive_range(200..=204)
    .add_expected_status(404);
let call = client.get("/items")?.with_expected_status_codes(codes);
Source

pub fn with_expected_status_code(self, status: StatusCode) -> Self

Sets expected status codes from a single http::StatusCode.

This method provides compile-time validation of status codes through the type system. Unlike the u16 variants, this method does not perform runtime validation since http::StatusCode guarantees valid HTTP status codes at compile time.

§Example
use clawspec_core::ApiClient;
use http::StatusCode;

let mut client = ApiClient::builder().build()?;

let call = client.get("/users")?
    .with_expected_status_code(StatusCode::OK);
Source

pub fn with_expected_status_code_range( self, range: RangeInclusive<StatusCode>, ) -> Self

Sets expected status codes from a range of http::StatusCode.

This method provides compile-time validation of status codes through the type system. Unlike the u16 variants, this method does not perform runtime validation since http::StatusCode guarantees valid HTTP status codes at compile time.

§Example
use clawspec_core::ApiClient;
use http::StatusCode;

let mut client = ApiClient::builder().build()?;

let call = client.get("/users")?
    .with_expected_status_code_range(StatusCode::OK..=StatusCode::NO_CONTENT);
Source

pub fn json<T>(self, t: &T) -> Result<Self, ApiClientError>
where T: Serialize + ToSchema + 'static,

Sets the request body to JSON.

This method serializes the provided data as JSON and sets the Content-Type header to application/json.

§Examples
#[derive(Serialize, ToSchema)]
struct CreateUser {
    name: String,
    email: String,
}

let mut client = ApiClient::builder().build()?;
let user_data = CreateUser {
    name: "John Doe".to_string(),
    email: "john@example.com".to_string(),
};

let call = client.post("/users")?.json(&user_data)?;
Source

pub fn form<T>(self, t: &T) -> Result<Self, ApiClientError>
where T: Serialize + ToSchema + 'static,

Sets the request body to form-encoded data.

This method serializes the provided data as application/x-www-form-urlencoded and sets the appropriate Content-Type header.

§Examples
#[derive(Serialize, ToSchema)]
struct LoginForm {
    username: String,
    password: String,
}

let mut client = ApiClient::builder().build()?;
let form_data = LoginForm {
    username: "user@example.com".to_string(),
    password: "secret".to_string(),
};

let call = client.post("/login")?.form(&form_data)?;
Source

pub fn raw(self, data: Vec<u8>, content_type: ContentType) -> Self

Sets the request body to raw binary data with a custom content type.

This method allows you to send arbitrary binary data with a specified content type. This is useful for sending data that doesn’t fit into the standard JSON or form categories.

§Examples
let mut client = ApiClient::builder().build()?;
// Send XML data
let xml_data = r#"<?xml version="1.0"?><user><name>John</name></user>"#;
let call = client.post("/import")?
    .raw(xml_data.as_bytes().to_vec(), ContentType::xml());

// Send binary file
let binary_data = vec![0xFF, 0xFE, 0xFD];
let call = client.post("/upload")?
    .raw(binary_data, ContentType::octet_stream());
Source

pub fn text(self, text: &str) -> Self

Sets the request body to plain text.

This is a convenience method for sending plain text data with text/plain content type.

§Examples
let mut client = ApiClient::builder().build()?;
let call = client.post("/notes")?.text("This is a plain text note");
Source

pub fn multipart(self, parts: Vec<(&str, &str)>) -> Self

Sets the request body to multipart/form-data.

This method creates a multipart body with a generated boundary and supports both text fields and file uploads. This is commonly used for file uploads or when combining different types of data in a single request.

§Examples
let mut client = ApiClient::builder().build()?;
let parts = vec![
    ("title", "My Document"),
    ("file", "file content here"),
];
let call = client.post("/upload")?.multipart(parts);

Trait Implementations§

Source§

impl Debug for ApiCall

Source§

fn fmt(&self, __derive_more_f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl IntoFuture for ApiCall

Implement IntoFuture for ApiCall to enable direct .await syntax

This provides a more ergonomic API by allowing direct .await on ApiCall:

let response = client.get("/users")?.await?;
Source§

type Output = Result<CallResult, ApiClientError>

The output that the future will produce on completion.
Source§

type IntoFuture = Pin<Box<dyn Future<Output = <ApiCall as IntoFuture>::Output> + Send>>

Which kind of future are we turning this into?
Source§

fn into_future(self) -> Self::IntoFuture

Creates a future from a value. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T> Instrument for T

Source§

fn instrument(self, span: Span) -> Instrumented<Self>

Instruments this type with the provided Span, returning an Instrumented wrapper. Read more
Source§

fn in_current_span(self) -> Instrumented<Self>

Instruments this type with the current Span, returning an Instrumented wrapper. Read more
Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> PolicyExt for T
where T: ?Sized,

Source§

fn and<P, B, E>(self, other: P) -> And<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow only if self and other return Action::Follow. Read more
Source§

fn or<P, B, E>(self, other: P) -> Or<T, P>
where T: Policy<B, E>, P: Policy<B, E>,

Create a new Policy that returns Action::Follow if either self or other returns Action::Follow. Read more
Source§

impl<T> Same for T

Source§

type Output = T

Should always be Self
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<T> WithSubscriber for T

Source§

fn with_subscriber<S>(self, subscriber: S) -> WithDispatch<Self>
where S: Into<Dispatch>,

Attaches the provided Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

fn with_current_subscriber(self) -> WithDispatch<Self>

Attaches the current default Subscriber to this type, returning a WithDispatch wrapper. Read more
Source§

impl<T> ErasedDestructor for T
where T: 'static,