Skip to content

Commit

Permalink
Add examples to rustdoc
Browse files Browse the repository at this point in the history
Signed-off-by: itowlson <[email protected]>
  • Loading branch information
itowlson committed Feb 12, 2025
1 parent 6ee0933 commit 1f31ad0
Show file tree
Hide file tree
Showing 6 changed files with 542 additions and 16 deletions.
3 changes: 3 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,6 @@ jobs:
shell: bash
run: cargo test --workspace

- name: Validate docs examples
shell: bash
run: cargo test --doc
135 changes: 134 additions & 1 deletion src/http.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,88 @@ use spin_executor::bindings::wasi::io::streams::{self, StreamError};

/// A unified request object that can represent both incoming and outgoing requests.
///
/// This should be used in favor of `IncomingRequest` and `OutgoingRequest` when there
/// This should be used in favor of [IncomingRequest] and [OutgoingRequest] when there
/// is no need for streaming bodies.
///
/// # Examples
///
/// Read the method, a header, and the body an incoming HTTP request, without streaming.
///
/// ```ignore
/// use spin_sdk::http::{Method, Request, Response};
/// use spin_sdk::http_component;
///
/// #[http_component]
/// fn handle_request(req: Request) -> anyhow::Result<Response> {
/// let method = req.method();
/// let content_type = req.header("content-type");
/// if *method == Method::Post {
/// let body = String::from_utf8_lossy(req.body());
/// }
/// todo!()
/// }
/// ```
///
/// Send an outgoing GET request (no body) to `example.com`.
///
/// ```no_run
/// use spin_sdk::http::{Request, Response};
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let request = Request::get("example.com");
/// let response: Response = spin_sdk::http::send(request).await?;
/// # Ok(())
/// # }
/// ```
///
/// Send an outgoing POST request with a non-streaming body to `example.com`.
///
/// ```no_run
/// use spin_sdk::http::{Request, Response};
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let request = Request::post("example.com", "it's a-me, Spin")
/// .header("content-type", "text/plain")
/// .build();
/// let response: Response = spin_sdk::http::send(request).await?;
/// # Ok(())
/// # }
/// ```
///
/// Build and send an outgoing request without using the helper shortcut.
///
/// ```no_run
/// use spin_sdk::http::{Method, Request, Response};
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let mut request = Request::new(Method::Put, "https://example.com/message/safety");
/// request.set_header("content-type", "text/plain");
/// *request.body_mut() = "beware the crocodile".as_bytes().to_vec();
/// let response: Response = spin_sdk::http::send(request).await?;
/// # Ok(())
/// # }
/// ```
///
/// Build and send an outgoing request using the fluent builder.
///
/// ```no_run
/// use spin_sdk::http::{Method, Request, Response};
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let request = Request::builder()
/// .uri("https://example.com/message/motivational")
/// .method(Method::Put)
/// .header("content-type", "text/plain")
/// .body("the capybaras of creativity fly higher than the bluebirds of banality")
/// .build();
/// let response: Response = spin_sdk::http::send(request).await?;
/// # Ok(())
/// # }
/// ```
pub struct Request {
/// The method of the request
method: Method,
Expand Down Expand Up @@ -613,6 +693,59 @@ impl ResponseOutparam {
}

/// Send an outgoing request
///
/// # Examples
///
/// Get the example.com home page:
///
/// ```no_run
/// use spin_sdk::http::{Request, Response};
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let request = Request::get("example.com").build();
/// let response: Response = spin_sdk::http::send(request).await?;
/// println!("{}", response.body().len());
/// # Ok(())
/// # }
/// ```
///
/// Use the `http` crate Request type to send a data transfer value:
///
/// ```no_run
/// use hyperium::Request;
///
/// #[derive(serde::Serialize)]
/// struct User {
/// name: String,
/// }
///
/// impl spin_sdk::http::conversions::TryIntoBody for User {
/// type Error = serde_json::Error;
///
/// fn try_into_body(self) -> Result<Vec<u8>, Self::Error> {
/// serde_json::to_vec(&self)
/// }
/// }
///
/// # #[tokio::main]
/// # async fn main() -> anyhow::Result<()> {
/// let user = User {
/// name: "Alice".to_owned(),
/// };
///
/// let request = hyperium::Request::builder()
/// .method("POST")
/// .uri("https://example.com/users")
/// .header("content-type", "application/json")
/// .body(user)?;
///
/// let response: hyperium::Response<()> = spin_sdk::http::send(request).await?;
///
/// println!("{}", response.status().is_success());
/// # Ok(())
/// # }
/// ```
pub async fn send<I, O>(request: I) -> Result<O, SendError>
where
I: TryIntoOutgoingRequest,
Expand Down
107 changes: 106 additions & 1 deletion src/key_value.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,73 @@
//! This module provides a generic interface for key-value storage, which may be implemented by the host various
//! ways (e.g. via an in-memory table, a local file, or a remote database). Details such as consistency model and
//! durability will depend on the implementation and may vary from one to store to the next.
//!
//! # Examples
//!
//! Open the default store and set the 'message' key:
//!
//! ```no_run
//! # fn main() -> anyhow::Result<()> {
//! let store = spin_sdk::key_value::Store::open_default()?;
//! store.set("message", "Hello world".as_bytes())?;
//! # Ok(())
//! # }
//! ```
use super::wit::v2::key_value;

#[cfg(feature = "json")]
use serde::{de::DeserializeOwned, Serialize};

#[doc(inline)]
pub use key_value::{Error, Store};
pub use key_value::Error;

/// An open key-value store.
///
/// # Examples
///
/// Open the default store and set the 'message' key:
///
/// ```no_run
/// # fn main() -> anyhow::Result<()> {
/// let store = spin_sdk::key_value::Store::open_default()?;
/// store.set("message", "Hello world".as_bytes())?;
/// # Ok(())
/// # }
/// ```
///
/// Open the default store and get the 'message' key:
///
/// ```no_run
/// # fn main() -> anyhow::Result<()> {
/// let store = spin_sdk::key_value::Store::open_default()?;
/// let message = store.get("message")?;
/// let response = message.unwrap_or_else(|| "not found".into());
/// # Ok(())
/// # }
/// ```
///
/// Open a named store and list all the keys defined in it:
///
/// ```no_run
/// # fn main() -> anyhow::Result<()> {
/// let store = spin_sdk::key_value::Store::open("finance")?;
/// let keys = store.get_keys()?;
/// # Ok(())
/// # }
/// ```
///
/// Open the default store and delete the 'message' key:
///
/// ```no_run
/// # fn main() -> anyhow::Result<()> {
/// let store = spin_sdk::key_value::Store::open_default()?;
/// store.delete("message")?;
/// # Ok(())
/// # }
/// ```
#[doc(inline)]
pub use key_value::Store;

impl Store {
/// Open the default store.
Expand All @@ -24,6 +83,31 @@ impl Store {
impl Store {
#[cfg(feature = "json")]
/// Serialize the given data to JSON, then set it as the value for the specified `key`.
///
/// # Examples
///
/// Open the default store and save a customer information document against the customer ID:
///
/// ```no_run
/// # use serde::{Deserialize, Serialize};
/// #[derive(Deserialize, Serialize)]
/// struct Customer {
/// name: String,
/// address: Vec<String>,
/// }
///
/// # fn main() -> anyhow::Result<()> {
/// let customer_id = "CR1234567";
/// let customer = Customer {
/// name: "Alice".to_owned(),
/// address: vec!["Wonderland Way".to_owned()],
/// };
///
/// let store = spin_sdk::key_value::Store::open_default()?;
/// store.set_json(customer_id, &customer)?;
/// # Ok(())
/// # }
/// ```
pub fn set_json<T: Serialize>(
&self,
key: impl AsRef<str>,
Expand All @@ -34,6 +118,27 @@ impl Store {

#[cfg(feature = "json")]
/// Deserialize an instance of type `T` from the value of `key`.
///
/// # Examples
///
/// Open the default store and retrieve a customer information document by customer ID:
///
/// ```no_run
/// # use serde::{Deserialize, Serialize};
/// #[derive(Deserialize, Serialize)]
/// struct Customer {
/// name: String,
/// address: Vec<String>,
/// }
///
/// # fn main() -> anyhow::Result<()> {
/// let customer_id = "CR1234567";
///
/// let store = spin_sdk::key_value::Store::open_default()?;
/// let customer = store.get_json::<Customer>(customer_id)?;
/// # Ok(())
/// # }
/// ```
pub fn get_json<T: DeserializeOwned>(
&self,
key: impl AsRef<str>,
Expand Down
14 changes: 8 additions & 6 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ pub mod key_value;
/// SQLite storage.
pub mod sqlite;

/// Large Language Model APIs
/// Large Language Model (Serverless AI) APIs
pub mod llm;

/// Exports the procedural macros for writing handlers for Spin components.
Expand Down Expand Up @@ -57,13 +57,13 @@ extern "C" fn __spin_sdk_hash() {}
/// Helpers for building Spin `wasi-http` components.
pub mod http;

/// Implementation of the spin mqtt interface.
/// MQTT messaging.
#[allow(missing_docs)]
pub mod mqtt {
pub use super::wit::v2::mqtt::{Connection, Error, Payload, Qos};
}

/// Implementation of the spin redis interface.
/// Redis storage and messaging.
#[allow(missing_docs)]
pub mod redis {
use std::hash::{Hash, Hasher};
Expand Down Expand Up @@ -99,16 +99,18 @@ pub mod redis {
}
}

/// Implementation of the spin postgres db interface.
/// Spin 2 Postgres relational database storage. Applications that do not require
/// Spin 2 support should use the `pg3` module instead.
pub mod pg;

/// Implementation of the spin postgres v3 db interface.
/// Postgres relational database storage.
pub mod pg3;

/// Implementation of the Spin MySQL database interface.
/// MySQL relational database storage.
pub mod mysql;

#[doc(inline)]
/// Component configuration variables.
pub use wit::v2::variables;

#[doc(hidden)]
Expand Down
50 changes: 46 additions & 4 deletions src/llm.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,49 @@
pub use crate::wit::v2::llm::{
self, EmbeddingsResult, EmbeddingsUsage, Error, InferencingParams, InferencingResult,
InferencingUsage,
};
pub use crate::wit::v2::llm::{Error, InferencingParams, InferencingResult, InferencingUsage};

/// Provides access to the underlying WIT interface. You should not normally need
/// to use this module: use the re-exports in this module instead.
#[doc(inline)]
pub use crate::wit::v2::llm;

/// The result of generating embeddings.
///
/// # Examples
///
/// Generate embeddings using the all-minilm-l6-v2 LLM.
///
/// ```no_run
/// use spin_sdk::llm;
///
/// # fn main() -> anyhow::Result<()> {
/// let text = &[
/// "I've just broken a priceless turnip".to_owned(),
/// ];
///
/// let embed_result = llm::generate_embeddings(llm::EmbeddingModel::AllMiniLmL6V2, text)?;
///
/// println!("prompt token count: {}", embed_result.usage.prompt_token_count);
/// println!("embedding: {:?}", embed_result.embeddings.first());
/// # Ok(())
/// # }
/// ```
#[doc(inline)]
pub use crate::wit::v2::llm::EmbeddingsResult;

/// Usage related to an embeddings generation request.
///
/// # Examples
///
/// ```no_run
/// use spin_sdk::llm;
///
/// # fn main() -> anyhow::Result<()> {
/// # let text = &[];
/// let embed_result = llm::generate_embeddings(llm::EmbeddingModel::AllMiniLmL6V2, text)?;
/// println!("prompt token count: {}", embed_result.usage.prompt_token_count);
/// # Ok(())
/// # }
/// ```
pub use crate::wit::v2::llm::EmbeddingsUsage;

/// The model use for inferencing
#[allow(missing_docs)]
Expand Down
Loading

0 comments on commit 1f31ad0

Please sign in to comment.