clawspec_core/_tutorial/chapter_4.rs
1//! # Chapter 4: Advanced Parameters
2//!
3//! This chapter covers headers, cookies, and advanced parameter serialization styles.
4//!
5//! ## Header Parameters
6//!
7//! Use [`CallHeaders`][crate::CallHeaders] to add request headers:
8//!
9//! ```rust,no_run
10//! use clawspec_core::{ApiClient, CallHeaders};
11//! # use serde::Deserialize;
12//! # use utoipa::ToSchema;
13//! # #[derive(Deserialize, ToSchema)]
14//! # struct Response { data: String }
15//!
16//! # #[tokio::main]
17//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
18//! # let mut client = ApiClient::builder().build()?;
19//! let headers = CallHeaders::new()
20//! .add_header("X-Request-ID", "req-12345")
21//! .add_header("X-Client-Version", "1.0.0")
22//! .add_header("Accept-Language", "en-US");
23//!
24//! let response: Response = client
25//! .get("/data")?
26//! .with_headers(headers)
27//! .await?
28//! .as_json()
29//! .await?;
30//! # Ok(())
31//! # }
32//! ```
33//!
34//! Headers are documented in the OpenAPI specification as header parameters.
35//!
36//! ## Cookie Parameters
37//!
38//! Use [`CallCookies`][crate::CallCookies] for cookie-based parameters:
39//!
40//! ```rust,no_run
41//! use clawspec_core::{ApiClient, CallCookies};
42//! # use serde::Deserialize;
43//! # use utoipa::ToSchema;
44//! # #[derive(Deserialize, ToSchema)]
45//! # struct Profile { name: String }
46//!
47//! # #[tokio::main]
48//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
49//! # let mut client = ApiClient::builder().build()?;
50//! let cookies = CallCookies::new()
51//! .add_cookie("session_id", "abc123")
52//! .add_cookie("user_id", 42)
53//! .add_cookie("preferences", vec!["dark_mode", "compact"]);
54//!
55//! let profile: Profile = client
56//! .get("/profile")?
57//! .with_cookies(cookies)
58//! .await?
59//! .as_json()
60//! .await?;
61//! # Ok(())
62//! # }
63//! ```
64//!
65//! ## Authentication
66//!
67//! Clawspec supports several authentication methods:
68//!
69//! ```rust,no_run
70//! use clawspec_core::{ApiClient, Authentication};
71//!
72//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
73//! // Bearer token (most common for APIs)
74//! let client = ApiClient::builder()
75//! .with_host("api.example.com")
76//! .with_authentication(Authentication::Bearer("your-api-token".into()))
77//! .build()?;
78//!
79//! // Basic authentication
80//! let client = ApiClient::builder()
81//! .with_host("api.example.com")
82//! .with_authentication(Authentication::Basic {
83//! username: "user".to_string(),
84//! password: "secret".into(),
85//! })
86//! .build()?;
87//!
88//! // API key in header
89//! let client = ApiClient::builder()
90//! .with_host("api.example.com")
91//! .with_authentication(Authentication::ApiKey {
92//! header_name: "X-API-Key".to_string(),
93//! key: "your-api-key".into(),
94//! })
95//! .build()?;
96//! # Ok(())
97//! # }
98//! ```
99//!
100//! You can override authentication per-request:
101//!
102//! ```rust,no_run
103//! # use clawspec_core::{ApiClient, Authentication};
104//! # #[tokio::main]
105//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
106//! let mut client = ApiClient::builder()
107//! .with_host("api.example.com")
108//! .with_authentication(Authentication::Bearer("default-token".into()))
109//! .build()?;
110//!
111//! // Use different auth for admin endpoints
112//! client.get("/admin/users")?
113//! .with_authentication(Authentication::Bearer("admin-token".into()))
114//! .await?;
115//!
116//! // Remove auth for public endpoints
117//! client.get("/public/status")?
118//! .with_authentication_none()
119//! .await?;
120//! # Ok(())
121//! # }
122//! ```
123//!
124//! ## Parameter Styles
125//!
126//! OpenAPI defines various serialization styles for parameters. Use [`ParamStyle`][crate::ParamStyle]:
127//!
128//! ### Path Parameter Styles
129//!
130//! ```rust
131//! use clawspec_core::{CallPath, ParamValue, ParamStyle};
132//!
133//! // Simple (default): /users/123
134//! let path = CallPath::from("/users/{id}")
135//! .add_param("id", ParamValue::new(123));
136//!
137//! // Label style: /users/.123
138//! let path = CallPath::from("/users/{id}")
139//! .add_param("id", ParamValue::with_style(123, ParamStyle::Label));
140//!
141//! // Matrix style: /users/;id=123
142//! let path = CallPath::from("/users/{id}")
143//! .add_param("id", ParamValue::with_style(123, ParamStyle::Matrix));
144//! ```
145//!
146//! ### Query Parameter Styles
147//!
148//! ```rust
149//! use clawspec_core::{CallQuery, ParamValue, ParamStyle};
150//!
151//! let tags = vec!["rust", "web", "api"];
152//!
153//! // Form (default): ?tags=rust&tags=web&tags=api
154//! let query = CallQuery::new()
155//! .add_param("tags", ParamValue::new(tags.clone()));
156//!
157//! // Space delimited: ?tags=rust%20web%20api
158//! let query = CallQuery::new()
159//! .add_param("tags", ParamValue::with_style(tags.clone(), ParamStyle::SpaceDelimited));
160//!
161//! // Pipe delimited: ?tags=rust|web|api
162//! let query = CallQuery::new()
163//! .add_param("tags", ParamValue::with_style(tags, ParamStyle::PipeDelimited));
164//! ```
165//!
166//! ### Deep Object Style
167//!
168//! For nested objects in query parameters:
169//!
170//! ```rust
171//! use clawspec_core::{CallQuery, ParamValue, ParamStyle};
172//!
173//! // Deep object: ?filter[status]=active&filter[type]=premium
174//! let filter = serde_json::json!({
175//! "status": "active",
176//! "type": "premium"
177//! });
178//!
179//! let query = CallQuery::new()
180//! .add_param("filter", ParamValue::with_style(filter, ParamStyle::DeepObject));
181//! ```
182//!
183//! ## Alternative Content Types
184//!
185//! Besides JSON, you can send other content types:
186//!
187//! ```rust,no_run
188//! use clawspec_core::ApiClient;
189//! use headers::ContentType;
190//! # use serde::Serialize;
191//! # use utoipa::ToSchema;
192//!
193//! #[derive(Serialize, ToSchema)]
194//! struct FormData {
195//! name: String,
196//! email: String,
197//! }
198//!
199//! # #[tokio::main]
200//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
201//! # let mut client = ApiClient::builder().build()?;
202//! // Form-encoded data
203//! let data = FormData {
204//! name: "Alice".to_string(),
205//! email: "alice@example.com".to_string(),
206//! };
207//! client.post("/form")?.form(&data)?.await?;
208//!
209//! // Raw bytes with custom content type
210//! let xml = r#"<user><name>Alice</name></user>"#;
211//! client.post("/xml")?
212//! .raw(xml.as_bytes().to_vec(), ContentType::xml())
213//! .await?;
214//!
215//! // Multipart form data
216//! let files = vec![
217//! ("file1", r#"{"data": "content1"}"#),
218//! ("file2", r#"{"data": "content2"}"#),
219//! ];
220//! client.post("/upload")?.multipart(files).await?;
221//! # Ok(())
222//! # }
223//! ```
224//!
225//! ## Key Points
226//!
227//! - [`CallHeaders`][crate::CallHeaders] and [`CallCookies`][crate::CallCookies] add
228//! documented parameters
229//! - Authentication can be set at client or request level
230//! - Parameter styles control OpenAPI serialization documentation
231//! - Multiple content types are supported (JSON, form, XML, multipart)
232//!
233//! Next: [Chapter 5: OpenAPI Customization][super::chapter_5] - Tags, descriptions,
234//! and metadata.