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
use std::collections::BTreeMap;
use std::net::SocketAddr;
use async_trait::async_trait;
use serde::{de::DeserializeOwned, Serialize};
pub use shuttle_common::{
database,
deployment::{DeploymentMetadata, Environment},
project::ProjectName,
resource::Type,
DatabaseReadyInfo, DbInput, DbOutput, SecretStore,
};
pub mod error;
pub use error::{CustomError, Error};
#[cfg(feature = "builder")]
pub mod builder;
/// Factories can be used to request the provisioning of additional resources (like databases).
///
/// An instance of factory is passed by the deployer as an argument to [ResourceBuilder::output] in the initial phase of deployment.
///
/// Also see the [shuttle_runtime::main] macro.
#[async_trait]
pub trait Factory: Send + Sync {
/// Get a database connection
async fn get_db_connection(
&mut self,
db_type: database::Type,
) -> Result<DatabaseReadyInfo, crate::Error>;
/// Get all the secrets for a service
async fn get_secrets(&mut self) -> Result<BTreeMap<String, String>, crate::Error>;
/// Get the metadata for this deployment
fn get_metadata(&self) -> DeploymentMetadata;
}
/// Used to get resources of type `T` from factories.
///
/// This is mainly meant for consumption by our code generator and should generally not be called by users.
///
/// ## Creating your own managed resource
///
/// You may want to create your own managed resource by implementing this trait for some builder `B` to construct resource `T`.
/// [`Factory`] can be used to provision resources on Shuttle's servers if your service will need any.
///
/// Please refer to `shuttle-examples/custom-resource` for examples of how to create custom resource. For more advanced provisioning
/// of custom resources, please [get in touch](https://discord.gg/shuttle) and detail your use case. We'll be interested to see what you
/// want to provision and how to do it on your behalf on the fly.
///
/// ```
#[async_trait]
pub trait ResourceBuilder<T> {
/// The type of resource this creates
const TYPE: Type;
/// The internal config being constructed by this builder. This will be used to find cached [Self::Output].
type Config: Serialize;
/// The output type used to build this resource later
type Output: Serialize + DeserializeOwned;
/// Create a new instance of this resource builder
fn new() -> Self;
/// Get the internal config state of the builder
///
/// If the exact same config was returned by a previous deployement that used this resource, then [Self::output()]
/// will not be called to get the builder output again. Rather the output state of the previous deployment
/// will be passed to [Self::build()].
fn config(&self) -> &Self::Config;
/// Get the config output of this builder
///
/// This method is where the actual resource provisioning should take place and is expected to take the longest. It
/// can at times even take minutes. That is why the output of this method is cached and calling this method can be
/// skipped as explained in [Self::config()].
async fn output(self, factory: &mut dyn Factory) -> Result<Self::Output, crate::Error>;
/// Build this resource from its config output
async fn build(build_data: &Self::Output) -> Result<T, crate::Error>;
}
/// The core trait of the shuttle platform. Every crate deployed to shuttle needs to implement this trait.
///
/// Use the [main][main] macro to expose your implementation to the deployment backend.
#[async_trait]
pub trait Service: Send {
/// This function is run exactly once on each instance of a deployment.
///
/// The deployer expects this instance of [Service][Service] to bind to the passed [SocketAddr][SocketAddr].
async fn bind(mut self, addr: SocketAddr) -> Result<(), error::Error>;
}