//! Easy boilerplate utilities for Rust http services which use [async-std][], [Tide][], [Surf][], and friends.
//!
//! Allows for service setup with feature-configured built-ins for maximum service consistency with low developer overhead,
//! and for easily integration testing the service without using a live network.
//!
//! **Scroll to the bottom for API Reference**
//!
//! ## Example
//!
//! ```no_run
//! use std::sync::Arc;
//!
//! use tide::{Request, Route};
//!
//! # #[allow(dead_code)]
//! struct AppState {
//! greeting: &'static str,
//! }
//!
//! # #[allow(dead_code)]
//! type AppRequest = Request<Arc<AppState>>;
//!
//! # #[allow(dead_code)]
//! async fn setup_app_state() -> preroll::SetupResult<AppState> {
//! Ok(AppState {
//! greeting: "Hello World!",
//! })
//! }
//!
//! # #[allow(dead_code)]
//! fn setup_routes(mut server: Route<'_, Arc<AppState>>) {
//! server
//! .at("hello-world")
//! .get(|req: AppRequest| async move {
//! Ok(req.state().greeting)
//! });
//! }
//!
//! // The "magic" happens here!
//! preroll::main!("hello-world", setup_app_state, setup_routes);
//! ```
//!
//! ## Features
//!
//! - Boilerplate `main` setup via [`preroll::main!`][], with optional features automatically configured.
//! - A [`preroll::prelude::*;`][] with all extension traits.
//! - Response logging with many details.
//! - Automatic JSON responses for errors in the form of [`JsonError`][].
//! - [Test utils][] with easy mock client setup.
//!
//! ## Optional features
//! Add-on features must be enabled via cargo features, e.g.
//!
//! ```toml
//! [dependencies.preroll]
//! version = "0.5"
//! features = ["honeycomb", "postgres"]
//! ```
//!
//! ### List of optional add-on features:
//! - `"honeycomb"`: Enables tracing to [honeycomb.io].
//! - Env variable `HONEYCOMBIO_WRITE_KEY` (required).
//! - Env variable `TRACELEVEL`, sets the tracing level filter, defaults to `info`.
//! - Writes to a dataset named `{service_name}-{environment}`.
//! - `service_name` is from `preroll::main!("service_name", ...)`.
//! - `environment` is from `ENVIRONMENT`, or defaults to `"development"`.
//! - `"lambda-http"`: Changes the HTTP listener to connect to an AWS Lambda execution environment.
//! - Is no longer reachable as a regular http server, but accepts http lambda requests as if it were one.
//! - Some environment variables, such as `PORT`, are disregarded.
//! - If the `"honeycomb"` feature is enabled, trace events are written to stdout, and must be collected via
//! a layer provided by Honeycomb. See: https://docs.honeycomb.io/getting-data-in/integrations/aws/aws-lambda/
//! - `"postgres"`: Enables a postgres connection pool with transactions.
//! - Env variable `PGURL`, which should be a properly formatted `postgres://` database url.
//! - Defaults to `"postgres://localhost/{service_name}"` (default postgres port).
//! - `service_name` is from `preroll::main!("service_name", ...)`.
//! - Env variable `PGMAXCONNECTIONS`, default 5 connections.
//! - Env variable `PGMAXLIFETIME`, default `30` (minutes).
//! - Enables [`PostgresRequestExt`][prelude::PostgresRequestExt] and [`test_utils::create_client_and_postgres`][].
//!
//! ### List of other optional features:
//! - `"panic-on-error"`: Makes the response logger [panic][] on error rather than log.
//! - Do not use in production. Prevents `--release` compilation.
//!
//! ## General Environment Settings
//! The following environment variables are read during `preroll::main!`:
//! - `ENVIRONMENT`: If this starts with `prod`, load the production-mode JSON logger, avoid `.env`.
//! - `FORCE_DOTENV`: Override production-mode, force-load environment from `.env`.
//! - `HOST`: Sets the hostname that this service will listen on. Defaults to `"127.0.0.1"`.
//! - `LOGLEVEL`: Set the logger's level filter, defaults to `info` in production-mode, `debug` in development-mode.
//! - `PORT`: Sets the port that this service will listen on. Defaults to `8080`.
//!
//! ## Note:
//!
//! This crate is intentionally somewhat prescriptive in how it templates a service and the interaction with
//! add-on features such as Postgres (via [SQLx][]).
//!
//! [`preroll::main!`]: https://docs.rs/preroll/0.8.0/preroll/macro.main.html
//! [`preroll::prelude::*;`]: https://docs.rs/preroll/0.8.0/preroll/prelude/index.html
//! [`JsonError`]: https://docs.rs/preroll/0.8.0/preroll/struct.JsonError.html
//! [async-std]: https://async.rs/
//! [honeycomb.io]: https://www.honeycomb.io/
//! [SQLx]: https://github.com/launchbadge/sqlx#sqlx
//! [Surf]: https://github.com/http-rs/surf#surf
//! [Test utils]: https://docs.rs/preroll/0.8.0/preroll/test_utils/index.html
//! [Tide]: https://github.com/http-rs/tide#tide
compile_error!;
pub
pub
pub
/// The format of error responses from preroll's error handling middleware.
pub use JsonError;
pub use VariadicRoutes;
/// The result type which is expected from functions passed to `preroll::main!`.
///
/// This is a `color_eyre::eyre::Result<T>`.
pub type SetupResult<T> = Result;
/// **Begin here.** A macro which constructs the equivalent of an `async fn main() {}`.
///
/// Automatically pulls in setup for preroll's default and optional features.
///
/// This macro takes the following arguments:
///
/// ## `service_name`
/// The constant service name, staticly set in the service's code.
///
/// An [**`&'static str`**](https://doc.rust-lang.org/std/primitive.str.html), e.g. `"service-name"`.
///
/// ## `state_setup` (optional)
/// This is where server state can be set.
///
/// An **`async fn setup_state() -> preroll::SetupResult<State>`**, where `State` is anything which can be thread-safe.
/// That is, the state must implement `Send + Sync`, (usually automatically), and must have the `'static` lifetime (must be [owned][]).
///
/// It is expected that `State` is some arbitrary custom type used by your service. `preroll` will wrap it in an [`Arc`][] so that it can be shared.
///
/// This function must be `async` and must return a `preroll::SetupResult`.
/// It is expected that setup could be anything and may need to await or error.
///
/// See [`tide::Server::with_state()`][] for more on Tide server state.
///
/// ## `custom_setup` (optional) (advanced)
/// Advanced, custom setup with access to the full server struct. Prefer using `routes_setup` whenever possible.
///
/// An **`async fn custom_setup(server: Server<Arc<State>>) -> SetupResult<Server<Arc<State>>>`**, where `State` is the type returned from `setup_state` or else the [unit `()`][] type.
///
/// ## `routes_setup` (one or more)
/// This is where routes should be set.
///
/// A **`fn setup_routes(server: &mut tide::Server<Arc<State>>)`**, where `State` is the type returned from `setup_state` or else the [unit `()`][] type.
/// It is expected that only Tide route handlers are set in this function. It must not be async and must not error.
///
/// ### API Versioning
///
/// Any number of `routes_setup` functions can be provided, by use of [`VariadicRoutes`][crate::VariadicRoutes], which will be API versioned as described below.
/// Usually this is done by putting the routes in a [Tuple][], similar to just adding more arguments but wrapping the routes arguments in parenthesis: `(routes_v1, routes_v2)`.
///
/// Preroll route setup functions are automatically namespaced under `/api/v{N}` where the `{N}` is the position of the routes setup function in
/// `preroll::main!`'s arguments, starting at `1`.
///
/// For example, `preroll::main!("my-service", my_routes)` will have `my_routes` mounted at `/api/v1`.
///
/// See [`tide::Server::at()`][] for more on Tide server routing.
///
/// # Basic Example
///
/// This will respond with `"Hello World!"` when a GET request is made to `$HOST:$PORT/api/v1/hello-world`.
///
/// ```no_run
/// # #[cfg(not(feature = "custom_middleware"))]
/// # {
/// use std::sync::Arc;
///
/// use tide::{Request, Route};
///
/// # #[allow(dead_code)]
/// struct AppState {
/// greeting: &'static str,
/// }
///
/// # #[allow(dead_code)]
/// type AppRequest = Request<Arc<AppState>>;
///
/// # #[allow(dead_code)]
/// async fn setup_app_state() -> preroll::SetupResult<AppState> {
/// Ok(AppState {
/// greeting: "Hello World!",
/// })
/// }
///
/// # #[allow(dead_code)]
/// fn setup_routes(mut server: Route<'_, Arc<AppState>>) {
/// server
/// .at("hello-world")
/// .get(|req: AppRequest| async move {
/// Ok(req.state().greeting)
/// });
/// }
///
/// preroll::main!("hello-world", setup_app_state, setup_routes);
/// # }
/// ```
///
/// # Full Example
/// With custom middleware and multiple api versions.
///
/// ```no_run
/// # #[cfg(not(feature = "custom_middleware"))]
/// # {
/// use std::sync::Arc;
///
/// use preroll::SetupResult;
/// use tide::{Request, Route, Server};
///
/// # #[allow(dead_code)]
/// pub struct AppState {
/// greeting: &'static str,
/// }
///
/// # #[allow(dead_code)]
/// type AppRequest = Request<Arc<AppState>>;
///
/// # #[allow(dead_code)]
/// async fn setup_app_state() -> preroll::SetupResult<AppState> {
/// Ok(AppState {
/// greeting: "Hello World!",
/// })
/// }
///
/// # #[allow(dead_code)]
/// pub async fn setup_custom(
/// server: Server<Arc<AppState>>
/// ) -> SetupResult<Server<Arc<AppState>>> {
/// // Adjust `server` in whichever ways neccessary
/// Ok(server)
/// }
///
/// # #[allow(dead_code)]
/// fn setup_routes_v1(mut server: Route<'_, Arc<AppState>>) {
/// server
/// .at("hello-world")
/// .get(|req: AppRequest| async move {
/// Ok(req.state().greeting)
/// });
/// }
///
/// # #[allow(dead_code)]
/// fn setup_routes_v2(mut server: Route<'_, Arc<AppState>>) {
/// server
/// .at("hello-world")
/// .get(|req: AppRequest| async move {
/// Ok("Hello from v2!")
/// });
/// }
///
/// preroll::main!(
/// "hello-world",
/// setup_app_state,
/// setup_custom,
/// (setup_routes_v1, setup_routes_v2)
/// );
/// # }
/// ```
///
/// [`tide::Server::at()`]: https://docs.rs/tide/0.15.0/tide/struct.Server.html#method.at
/// [`tide::Server::with_state()`]: https://docs.rs/tide/0.15.0/tide/struct.Server.html#method.with_state
/// [unit `()`]: https://doc.rust-lang.org/std/primitive.unit.html
/// [`Arc`]: https://doc.rust-lang.org/std/sync/struct.Arc.html
/// [owned]: https://doc.rust-lang.org/book/ch04-00-understanding-ownership.html
/// [Tuple]: https://doc.rust-lang.org/std/primitive.tuple.html