1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
//! # Chapter 1: Getting Started
//!
//! In this chapter, you'll learn how to create an API client and make your first request.
//!
//! ## Creating the Client
//!
//! The [`ApiClient`][crate::ApiClient] is your main entry point for making requests.
//! Use the builder pattern to configure it:
//!
//! ```rust,no_run
//! use clawspec_core::ApiClient;
//!
//! # fn main() -> Result<(), Box<dyn std::error::Error>> {
//! // Minimal client (connects to localhost:80)
//! let client = ApiClient::builder().build()?;
//!
//! // Client with custom host
//! let client = ApiClient::builder()
//! .with_host("api.example.com")
//! .build()?;
//!
//! // Client with host and port
//! let client = ApiClient::builder()
//! .with_host("api.example.com")
//! .with_port(8080)
//! .build()?;
//!
//! // Client with base path (all requests will be prefixed)
//! let client = ApiClient::builder()
//! .with_host("api.example.com")
//! .with_base_path("/api/v1")?
//! .build()?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Your First GET Request
//!
//! Let's make a simple GET request. You'll need a response type that implements
//! [`Deserialize`][serde::Deserialize] and [`ToSchema`][utoipa::ToSchema]:
//!
//! ```rust,no_run
//! use clawspec_core::ApiClient;
//! use serde::Deserialize;
//! use utoipa::ToSchema;
//!
//! #[derive(Deserialize, ToSchema)]
//! struct User {
//! id: u64,
//! name: String,
//! email: String,
//! }
//!
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut client = ApiClient::builder()
//! .with_host("api.example.com")
//! .build()?;
//!
//! // Make a GET request
//! let user: User = client
//! .get("/users/123")? // Create the request
//! .await? // Send it (using IntoFuture)
//! .as_json() // Parse response as JSON
//! .await?;
//!
//! println!("Got user: {} ({})", user.name, user.email);
//! # Ok(())
//! # }
//! ```
//!
//! ## Understanding the Flow
//!
//! Let's break down what happens:
//!
//! 1. **`client.get("/users/123")?`** - Creates an [`ApiCall`][crate::ApiCall] builder
//! 2. **`.await?`** - Sends the request (via [`IntoFuture`])
//! 3. **`.as_json().await?`** - Parses the response body as JSON
//!
//! The schema for `User` is automatically captured when you call `.as_json()`.
//!
//! ## Generating the OpenAPI Spec
//!
//! After making requests, you can generate the OpenAPI specification:
//!
//! ```rust,no_run
//! # use clawspec_core::ApiClient;
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! let mut client = ApiClient::builder().build()?;
//!
//! // ... make some requests ...
//!
//! // Get the collected OpenAPI spec
//! let openapi = client.collected_openapi().await;
//!
//! // Output as JSON
//! println!("{}", openapi.to_pretty_json()?);
//! # Ok(())
//! # }
//! ```
//!
//! ### YAML Output
//!
//! To output YAML instead of JSON, enable the `yaml` feature in your `Cargo.toml`:
//!
//! ```toml
//! [dependencies]
//! clawspec-core = { version = "...", features = ["yaml"] }
//! ```
//!
//! Then use the [`ToYaml`][crate::ToYaml] trait:
//!
//! ```rust,ignore
//! use clawspec_core::ToYaml;
//!
//! let yaml = openapi.to_yaml()?;
//! println!("{yaml}");
//! ```
//!
//! ## Response Without Parsing
//!
//! Sometimes you don't need to parse the response body:
//!
//! ```rust,no_run
//! # use clawspec_core::ApiClient;
//! # #[tokio::main]
//! # async fn main() -> Result<(), Box<dyn std::error::Error>> {
//! # let mut client = ApiClient::builder().build()?;
//! // Get raw response details
//! let raw = client.get("/health")?.await?.as_raw().await?;
//! println!("Status: {}", raw.status_code());
//! println!("Body: {:?}", raw.text());
//!
//! // Or just consume the response without reading the body
//! client.get("/ping")?.await?.as_empty().await?;
//! # Ok(())
//! # }
//! ```
//!
//! ## Key Points
//!
//! - Use [`ApiClient::builder()`][crate::ApiClient::builder] to create clients
//! - Response types need `#[derive(Deserialize, ToSchema)]`
//! - Call `.as_json()` to parse responses and capture schemas
//! - Use `collected_openapi()` to get the generated spec
//!
//! Next: [Chapter 2: Request Building][super::chapter_2] - Learn about POST requests and parameters.