Skip to content

Commit 00d693f

Browse files
committed
Add examples to rustdoc
Signed-off-by: itowlson <[email protected]>
1 parent 6ee0933 commit 00d693f

File tree

7 files changed

+782
-21
lines changed

7 files changed

+782
-21
lines changed

.github/workflows/build.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,3 +39,6 @@ jobs:
3939
shell: bash
4040
run: cargo test --workspace
4141

42+
- name: Validate docs examples
43+
shell: bash
44+
run: cargo test --doc

src/http.rs

Lines changed: 259 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -7,19 +7,146 @@ use std::collections::HashMap;
77
pub use conversions::IntoResponse;
88
#[doc(inline)]
99
pub use types::{
10-
ErrorCode, Fields, Headers, IncomingRequest, IncomingResponse, Method, OutgoingBody,
11-
OutgoingRequest, OutgoingResponse, Scheme, StatusCode, Trailers,
10+
ErrorCode, Fields, Headers, IncomingResponse, Method, OutgoingBody, OutgoingRequest,
11+
OutgoingResponse, Scheme, StatusCode, Trailers,
1212
};
1313

1414
use self::conversions::{TryFromIncomingResponse, TryIntoOutgoingRequest};
1515
use super::wit::wasi::http0_2_0::types;
1616
use futures::SinkExt;
1717
use spin_executor::bindings::wasi::io::streams::{self, StreamError};
1818

19+
/// Represents an incoming HTTP request.
20+
///
21+
/// If you don't need streaming access to the request body, you may find it
22+
/// easier to work with [Request] instead. To make outgoing requests, use
23+
/// [Request] (non-streaming) or [OutgoingRequest].
24+
///
25+
/// # Examples
26+
///
27+
/// Access the request body as a Rust stream:
28+
///
29+
/// ```no_run
30+
/// # use spin_sdk::http::{IncomingRequest, ResponseOutparam};
31+
/// async fn handle_request(req: IncomingRequest, response_outparam: ResponseOutparam) {
32+
/// use futures::stream::StreamExt;
33+
///
34+
/// let mut stream = req.into_body_stream();
35+
/// loop {
36+
/// let chunk = stream.next().await;
37+
/// match chunk {
38+
/// None => {
39+
/// println!("end of request body");
40+
/// break;
41+
/// }
42+
/// Some(Ok(chunk)) => {
43+
/// // process the data from the stream in a very realistic way
44+
/// println!("read {} bytes", chunk.len());
45+
/// }
46+
/// Some(Err(e)) => {
47+
/// println!("error reading body: {e:?}");
48+
/// break;
49+
/// }
50+
/// }
51+
/// }
52+
/// }
53+
/// ```
54+
///
55+
/// Access the body in a non-streaming way. This can be useful where your component
56+
/// must take IncomingRequest because some scenarios need streaming, but you
57+
/// have other scenarios that do not.
58+
///
59+
/// ```no_run
60+
/// # use spin_sdk::http::{IncomingRequest, ResponseOutparam};
61+
/// async fn handle_request(req: IncomingRequest, response_outparam: ResponseOutparam) {
62+
/// let body = req.into_body().await.unwrap();
63+
/// }
64+
/// ```
65+
#[doc(inline)]
66+
pub use types::IncomingRequest;
67+
1968
/// A unified request object that can represent both incoming and outgoing requests.
2069
///
21-
/// This should be used in favor of `IncomingRequest` and `OutgoingRequest` when there
70+
/// This should be used in favor of [IncomingRequest] and [OutgoingRequest] when there
2271
/// is no need for streaming bodies.
72+
///
73+
/// # Examples
74+
///
75+
/// Read the method, a header, and the body an incoming HTTP request, without streaming:
76+
///
77+
/// ```no_run
78+
/// # use spin_sdk::http::{Method, Request, Response};
79+
///
80+
/// fn handle_request(req: Request) -> anyhow::Result<Response> {
81+
/// let method = req.method();
82+
/// let content_type = req.header("content-type");
83+
/// if *method == Method::Post {
84+
/// let body = String::from_utf8_lossy(req.body());
85+
/// }
86+
/// todo!()
87+
/// }
88+
/// ```
89+
///
90+
/// Send an outgoing GET request (no body) to `example.com`:
91+
///
92+
/// ```no_run
93+
/// use spin_sdk::http::{Request, Response};
94+
///
95+
/// # #[tokio::main]
96+
/// # async fn main() -> anyhow::Result<()> {
97+
/// let request = Request::get("https://example.com");
98+
/// let response: Response = spin_sdk::http::send(request).await?;
99+
/// # Ok(())
100+
/// # }
101+
/// ```
102+
///
103+
/// Send an outgoing POST request with a non-streaming body to `example.com`.
104+
///
105+
/// ```no_run
106+
/// use spin_sdk::http::{Request, Response};
107+
///
108+
/// # #[tokio::main]
109+
/// # async fn main() -> anyhow::Result<()> {
110+
/// let request = Request::post("https://example.com", "it's a-me, Spin")
111+
/// .header("content-type", "text/plain")
112+
/// .build();
113+
/// let response: Response = spin_sdk::http::send(request).await?;
114+
/// # Ok(())
115+
/// # }
116+
/// ```
117+
///
118+
/// Build and send an outgoing request without using the helper shortcut.
119+
///
120+
/// ```no_run
121+
/// use spin_sdk::http::{Method, Request, Response};
122+
///
123+
/// # #[tokio::main]
124+
/// # async fn main() -> anyhow::Result<()> {
125+
/// let mut request = Request::new(Method::Put, "https://example.com/message/safety");
126+
/// request.set_header("content-type", "text/plain");
127+
/// *request.body_mut() = "beware the crocodile".as_bytes().to_vec();
128+
/// let response: Response = spin_sdk::http::send(request).await?;
129+
/// # Ok(())
130+
/// # }
131+
/// ```
132+
///
133+
/// Build and send an outgoing request using the fluent builder.
134+
///
135+
/// ```no_run
136+
/// use spin_sdk::http::{Method, Request, Response};
137+
///
138+
/// # #[tokio::main]
139+
/// # async fn main() -> anyhow::Result<()> {
140+
/// let request = Request::builder()
141+
/// .uri("https://example.com/message/motivational")
142+
/// .method(Method::Put)
143+
/// .header("content-type", "text/plain")
144+
/// .body("the capybaras of creativity fly higher than the bluebirds of banality")
145+
/// .build();
146+
/// let response: Response = spin_sdk::http::send(request).await?;
147+
/// # Ok(())
148+
/// # }
149+
/// ```
23150
pub struct Request {
24151
/// The method of the request
25152
method: Method,
@@ -179,7 +306,44 @@ impl Request {
179306
}
180307
}
181308

182-
/// A request builder
309+
/// A builder for non-streaming outgoing HTTP requests. You can obtain
310+
/// a RequestBuilder from the [Request::builder()] method, or from
311+
/// method-specific helpers such as [Request::get()] or [Request::post()].
312+
///
313+
/// # Examples
314+
///
315+
/// Use a method helper to build an outgoing POST request:
316+
///
317+
/// ```no_run
318+
/// use spin_sdk::http::{Request, Response};
319+
///
320+
/// # #[tokio::main]
321+
/// # async fn main() -> anyhow::Result<()> {
322+
/// let request = Request::post("https://example.com", "it's a-me, Spin")
323+
/// .header("content-type", "text/plain")
324+
/// .build();
325+
/// let response: Response = spin_sdk::http::send(request).await?;
326+
/// # Ok(())
327+
/// # }
328+
/// ```
329+
///
330+
/// Build and send an outgoing request using the RequestBuilder.
331+
///
332+
/// ```no_run
333+
/// use spin_sdk::http::{Method, Request, Response};
334+
///
335+
/// # #[tokio::main]
336+
/// # async fn main() -> anyhow::Result<()> {
337+
/// let request = Request::builder()
338+
/// .uri("https://example.com/message/motivational")
339+
/// .method(Method::Put)
340+
/// .header("content-type", "text/plain")
341+
/// .body("the capybaras of creativity fly higher than the bluebirds of banality")
342+
/// .build();
343+
/// let response: Response = spin_sdk::http::send(request).await?;
344+
/// # Ok(())
345+
/// # }
346+
/// ```
183347
pub struct RequestBuilder {
184348
request: Request,
185349
}
@@ -234,6 +398,43 @@ impl RequestBuilder {
234398
///
235399
/// This should be used in favor of `OutgoingResponse` and `IncomingResponse` when there
236400
/// is no need for streaming bodies.
401+
///
402+
/// # Examples
403+
///
404+
/// Send a response to an incoming HTTP request:
405+
///
406+
/// ```no_run
407+
/// use spin_sdk::http::{Request, Response};
408+
///
409+
/// fn handle_request(req: Request) -> anyhow::Result<Response> {
410+
/// Ok(Response::builder()
411+
/// .status(200)
412+
/// .header("content-type", "text/plain")
413+
/// .body("Hello, world")
414+
/// .build())
415+
/// }
416+
/// ```
417+
///
418+
/// Parse a response from an outgoing HTTP request:
419+
///
420+
/// ```no_run
421+
/// # use spin_sdk::http::{Request, Response};
422+
/// #[derive(serde::Deserialize)]
423+
/// struct User {
424+
/// name: String,
425+
/// }
426+
///
427+
/// # #[tokio::main]
428+
/// # async fn main() -> anyhow::Result<()> {
429+
/// let request = Request::get("https://example.com");
430+
/// let response: Response = spin_sdk::http::send(request).await?;
431+
/// if *response.status() == 200 {
432+
/// let body = response.body();
433+
/// let user: User = serde_json::from_slice(body)?;
434+
/// }
435+
/// # Ok(())
436+
/// # }
437+
/// ```
237438
pub struct Response {
238439
/// The status of the response
239440
status: StatusCode,
@@ -613,6 +814,59 @@ impl ResponseOutparam {
613814
}
614815

615816
/// Send an outgoing request
817+
///
818+
/// # Examples
819+
///
820+
/// Get the example.com home page:
821+
///
822+
/// ```no_run
823+
/// use spin_sdk::http::{Request, Response};
824+
///
825+
/// # #[tokio::main]
826+
/// # async fn main() -> anyhow::Result<()> {
827+
/// let request = Request::get("example.com").build();
828+
/// let response: Response = spin_sdk::http::send(request).await?;
829+
/// println!("{}", response.body().len());
830+
/// # Ok(())
831+
/// # }
832+
/// ```
833+
///
834+
/// Use the `http` crate Request type to send a data transfer value:
835+
///
836+
/// ```no_run
837+
/// use hyperium::Request;
838+
///
839+
/// #[derive(serde::Serialize)]
840+
/// struct User {
841+
/// name: String,
842+
/// }
843+
///
844+
/// impl spin_sdk::http::conversions::TryIntoBody for User {
845+
/// type Error = serde_json::Error;
846+
///
847+
/// fn try_into_body(self) -> Result<Vec<u8>, Self::Error> {
848+
/// serde_json::to_vec(&self)
849+
/// }
850+
/// }
851+
///
852+
/// # #[tokio::main]
853+
/// # async fn main() -> anyhow::Result<()> {
854+
/// let user = User {
855+
/// name: "Alice".to_owned(),
856+
/// };
857+
///
858+
/// let request = hyperium::Request::builder()
859+
/// .method("POST")
860+
/// .uri("https://example.com/users")
861+
/// .header("content-type", "application/json")
862+
/// .body(user)?;
863+
///
864+
/// let response: hyperium::Response<()> = spin_sdk::http::send(request).await?;
865+
///
866+
/// println!("{}", response.status().is_success());
867+
/// # Ok(())
868+
/// # }
869+
/// ```
616870
pub async fn send<I, O>(request: I) -> Result<O, SendError>
617871
where
618872
I: TryIntoOutgoingRequest,
@@ -692,7 +946,7 @@ impl std::fmt::Display for NonUtf8BodyError {
692946
}
693947

694948
mod router;
695-
/// Exports HTTP Router items.
949+
#[doc(inline)]
696950
pub use router::*;
697951

698952
/// A Body extractor

0 commit comments

Comments
 (0)