Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

docs: updates for return types for examples actix-web, tide, serenity, tower #892

Merged
merged 17 commits into from Oct 28, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
40 changes: 40 additions & 0 deletions codegen/src/lib.rs
Expand Up @@ -63,6 +63,46 @@ pub fn main(
shuttle_main::r#impl(attr, item)
}

/// Generates a wasm32-wasi module containing an Axum router with your endpoints, which is passed as a
/// hyper::service::Service to a hyper::Server.
///
/// ## Example
///
/// ```
/// shuttle_next::app! {
/// use futures::TryStreamExt;
/// use tracing::debug;
/// use shuttle_next::body::StreamBody;
/// use shuttle_next::extract::BodyStream;
/// use shuttle_next::response::{Response, IntoResponse};
///
/// #[shuttle_next::endpoint(method = get, route = "/")]
/// async fn hello() -> &'static str {
/// "Hello, World!"
/// }
///
/// // We can also use tracing/log macros directly:
/// #[shuttle_next::endpoint(method = get, route = "/goodbye")]
/// async fn goodbye() -> &'static str {
/// debug!("goodbye endpoint called");
/// "Goodbye, World!"
/// }
///
/// // We can also extract the http body in our handlers.
/// // The endpoint below takes the body from the request using the axum `BodyStream`
/// // extractor, lazily maps its bytes to uppercase and streams it back in our response:
/// #[shuttle_next::endpoint(method = post, route = "/uppercase")]
/// async fn uppercase(body: BodyStream) -> impl IntoResponse {
/// let chunk_stream = body.map_ok(|chunk| {
/// chunk
/// .iter()
/// .map(|byte| byte.to_ascii_uppercase())
/// .collect::<Vec<u8>>()
/// });
/// Response::new(StreamBody::new(chunk_stream))
/// }
/// }
/// ```
#[cfg(feature = "next")]
#[proc_macro_error::proc_macro_error]
#[proc_macro]
Expand Down
25 changes: 22 additions & 3 deletions resources/secrets/README.md
@@ -1,13 +1,32 @@
# Shuttle Secrets

This plugin manages secrets on [shuttle](https://www.shuttle.rs).
This plugin manages secrets on [Shuttle](https://www.shuttle.rs).

## Usage

Add `shuttle-secrets` to the dependencies for your service, and add a `Secrets.toml` to the root of your project
with the secrets you'd like to store. Make sure to add `Secrets.toml` to a `.gitignore` to omit your secrets from version control.
with the secrets you'd like to store. Make sure to add `Secrets*.toml` to a `.gitignore` to omit your secrets from version control.

Next, pass `#[shuttle_secrets::Secrets] secret_store: SecretStore` as an argument to your `shuttle_service::main` function.
`SecretStore::get` can now be called to retrieve your API keys and other secrets at runtime.

An example using the Rocket framework can be found on [GitHub](https://github.com/shuttle-hq/shuttle-examples/tree/main/rocket/secrets)
## Example

```rust,ignore
#[shuttle_runtime::main]
async fn rocket(
#[shuttle_secrets::Secrets] secret_store: SecretStore,
) -> ShuttleRocket {
// get secret defined in `Secrets.toml` file.
let secret = if let Some(secret) = secret_store.get("MY_API_KEY") {
secret
} else {
return Err(anyhow!("secret was not found").into());
};

let state = MyState { secret };
let rocket = rocket::build().mount("/", routes![secret]).manage(state);

Ok(rocket.into())
}
```
4 changes: 3 additions & 1 deletion resources/secrets/src/lib.rs
@@ -1,9 +1,11 @@
use async_trait::async_trait;
#![doc = include_str!("../README.md")]

use async_trait::async_trait;
use serde::Serialize;
pub use shuttle_service::SecretStore;
use shuttle_service::{Error, Factory, ResourceBuilder, Type};

/// A struct that represents service secrets
#[derive(Serialize)]
pub struct Secrets;

Expand Down
3 changes: 2 additions & 1 deletion resources/shared-db/README.md
@@ -1,6 +1,7 @@
# Shuttle Shared Databases

This plugin manages databases that are shared with other services on [shuttle](https://www.shuttle.rs).
This plugin manages databases that are shared with other services on [Shuttle](https://www.shuttle.rs).
Your database will be in a cluster shared with other users, but it will not be accessible by other users.

## Usage

Expand Down
1 change: 1 addition & 0 deletions resources/shared-db/src/postgres.rs
Expand Up @@ -4,6 +4,7 @@ use shuttle_service::{
database, error::CustomError, DbInput, DbOutput, Error, Factory, ResourceBuilder, Type,
};

/// Handles the state of a Shuttle managed Postgres DB and sets up a Postgres driver.
#[derive(Serialize)]
pub struct Postgres {
config: DbInput,
Expand Down
1 change: 1 addition & 0 deletions service/src/error.rs
Expand Up @@ -18,4 +18,5 @@ pub enum Error {
Custom(#[from] CustomError),
}

/// Type alias for an `anyhow::Error`.
pub type CustomError = anyhow::Error;
4 changes: 3 additions & 1 deletion services/README.md
@@ -1,5 +1,7 @@
## Service Integrations
# Service Integrations

The list of supported frameworks for shuttle is always growing. If you feel we are missing a framework you would like, then feel to create a feature request for your desired framework.

## Writing your own service integration

Creating your own service integration is quite simple. You only need to implement the [`Service`](https://docs.rs/shuttle-service/latest/shuttle_service/trait.Service.html) trait for your framework.
20 changes: 17 additions & 3 deletions services/shuttle-actix-web/src/lib.rs
@@ -1,5 +1,7 @@
//! Shuttle service integration for the Actix Web framework.
//!
//! ## Example
//!
//! ```rust,no_run
//! use actix_web::{get, web::ServiceConfig};
//! use shuttle_actix_web::ShuttleActixWeb;
Expand All @@ -10,15 +12,15 @@
//! }
//!
//! #[shuttle_runtime::main]
//! async fn actix_web(
//! ) -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
//! async fn actix_web() -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
//! let config = move |cfg: &mut ServiceConfig| {
//! cfg.service(hello_world);
//! };
//!
//! Ok(config.into())
//! }
//! ```

use std::net::SocketAddr;

/// A wrapper type for a closure that returns an [actix_web::web::ServiceConfig] so we can implement
Expand Down Expand Up @@ -56,5 +58,17 @@ where
}
}

/// The return type that should be returned from the [shuttle_runtime::main] function.
/// Return type from the `[shuttle_runtime::main]` macro for an Actix-based service.
///
/// # Example
/// ```rust,no_run
/// # use shuttle_actix_web::ShuttleActixWeb;
/// # use actix_web::web::ServiceConfig;
///
/// #[shuttle_runtime::main]
/// async fn example_service() -> ShuttleActixWeb<impl FnOnce(&mut ServiceConfig) + Send + Clone + 'static> {
/// let config = move |_cfg: &mut ServiceConfig| {};
/// Ok(config.into())
/// }
/// ```
pub type ShuttleActixWeb<F> = Result<ActixWebService<F>, shuttle_runtime::Error>;
2 changes: 2 additions & 0 deletions services/shuttle-next/src/lib.rs
@@ -1,3 +1,5 @@
//! [shuttle_next](https://docs.shuttle.rs/examples/shuttle-next)
//! A batteries-included, WASM-based backend web-framework.
pub use axum::*;
pub use futures_executor::block_on;
pub use http::Request;
Expand Down
54 changes: 52 additions & 2 deletions services/shuttle-serenity/src/lib.rs
@@ -1,5 +1,7 @@
//! Shuttle service integration for the Serenity discord bot framework.
//!
//! ## Example
//!
//! ```rust,no_run
//! use anyhow::anyhow;
//! use serenity::async_trait;
Expand Down Expand Up @@ -47,8 +49,8 @@
//!
//! Ok(client.into())
//! }
//!
//! ```

use shuttle_runtime::{CustomError, Error};
use std::net::SocketAddr;

Expand All @@ -72,5 +74,53 @@ impl From<serenity::Client> for SerenityService {
}
}

/// The return type that should be returned from the [shuttle_runtime::main] function.
/// Return type from the `[shuttle_runtime::main]` macro for a Serenity-based service.
///
/// ## Example
///
/// ```rust,no_run
/// # use anyhow::anyhow;
/// # use serenity::async_trait;
/// # use serenity::model::channel::Message;
/// # use serenity::model::gateway::Ready;
/// # use serenity::prelude::*;
/// # use shuttle_secrets::SecretStore;
/// # use tracing::{error, info};
/// # struct Bot;
/// # #[async_trait]
/// # impl EventHandler for Bot {
/// # async fn message(&self, ctx: Context, msg: Message) {
/// # if msg.content == "!hello" {
/// # if let Err(e) = msg.channel_id.say(&ctx.http, "world!").await {
/// # error!("Error sending message: {:?}", e);
/// # }
/// # }
/// # }
/// # async fn ready(&self, _: Context, ready: Ready) {
/// # info!("{} is connected!", ready.user.name);
/// # }
/// # }
///
/// #[shuttle_runtime::main]
/// async fn serenity(
/// #[shuttle_secrets::Secrets] secret_store: SecretStore,
/// ) -> shuttle_serenity::ShuttleSerenity {
/// // Get the discord token set in `Secrets.toml`
/// let token = if let Some(token) = secret_store.get("DISCORD_TOKEN") {
/// token
/// } else {
/// return Err(anyhow!("'DISCORD_TOKEN' was not found").into());
/// };
///
/// // Set gateway intents, which decides what events the bot will be notified about
/// let intents = GatewayIntents::GUILD_MESSAGES | GatewayIntents::MESSAGE_CONTENT;
///
/// let client = Client::builder(&token, intents)
/// .event_handler(Bot)
/// .await
/// .expect("Err creating client");
///
/// Ok(client.into())
/// }
/// ```
pub type ShuttleSerenity = Result<SerenityService, Error>;
20 changes: 19 additions & 1 deletion services/shuttle-tide/src/lib.rs
@@ -1,5 +1,7 @@
//! Shuttle service integration for the Tide web framework.
//!
//! ## Example
//!
//! ```rust,no_run
//! #[shuttle_runtime::main]
//! async fn tide() -> shuttle_tide::ShuttleTide<()> {
Expand Down Expand Up @@ -36,5 +38,21 @@ impl<T> From<tide::Server<T>> for TideService<T> {
Self(router)
}
}
/// The return type that should be returned from the [shuttle_runtime::main] function.

/// Return type from the `[shuttle_runtime::main]` macro for a Tide-based service.
///
/// ## Example
///
/// ```rust,no_run
/// # use shuttle_tide::ShuttleTide;
///
/// #[shuttle_runtime::main]
/// async fn tide() -> ShuttleTide<()> {
/// let mut app = tide::new();
/// app.with(tide::log::LogMiddleware::new());
/// app.at("/").get(|_| async { Ok("Hello, world!") });
///
/// Ok(app.into())
/// }
/// ```
pub type ShuttleTide<T> = Result<TideService<T>, Error>;
43 changes: 42 additions & 1 deletion services/shuttle-tower/src/lib.rs
@@ -1,5 +1,7 @@
//! Shuttle service integration for the Tower framework.
//!
//! ## Example
//!
//! ```rust,no_run
//! use std::convert::Infallible;
//! use std::future::Future;
Expand Down Expand Up @@ -83,5 +85,44 @@ where
}
}

/// The return type that should be returned from the [shuttle_runtime::main] function.
/// Shuttle service return type for the Tower framework.
///
/// ## Example
///
/// ```rust,no_run
/// # use std::convert::Infallible;
/// # use std::future::Future;
/// # use std::pin::Pin;
/// # use std::task::{Context, Poll};
///
/// #[derive(Clone)]
/// struct HelloWorld;
///
/// impl tower::Service<hyper::Request<hyper::Body>> for HelloWorld {
/// type Response = hyper::Response<hyper::Body>;
/// type Error = Infallible;
/// type Future = Pin<Box<dyn Future<Output = Result<Self::Response, Self::Error>> + Send + Sync>>;
///
/// fn poll_ready(&mut self, _cx: &mut Context<'_>) -> Poll<Result<(), Self::Error>> {
/// Poll::Ready(Ok(()))
/// }
///
/// fn call(&mut self, _req: hyper::Request<hyper::Body>) -> Self::Future {
/// let body = hyper::Body::from("Hello, world!");
/// let resp = hyper::Response::builder()
/// .status(200)
/// .body(body)
/// .expect("Unable to create the `hyper::Response` object");
/// let fut = async { Ok(resp) };
/// Box::pin(fut)
/// }
/// }
///
/// #[shuttle_runtime::main]
/// async fn tower() -> shuttle_tower::ShuttleTower<HelloWorld> {
/// let service = HelloWorld;
///
/// Ok(service.into())
/// }
/// ```
pub type ShuttleTower<T> = Result<TowerService<T>, Error>;