mod app_trait;
mod health;
mod include;
mod run;
mod scope;
mod service;
pub use app_trait::App;
pub use health::{HealthProbe, HealthState};
#[doc(hidden)]
pub use include::{IncludeMount, ScopeCommit};
pub use include::{Mounting, MountingSlots};
pub use run::RunningApp;
pub use scope::BrokerScope;
#[cfg(feature = "testing")]
pub(crate) use service::{RegisteredBroker, TestParts};
pub use service::{RustStream, Setup, Wired};
use std::sync::Arc;
use thiserror::Error;
use tokio::task::JoinHandle;
use crate::describe::{AppId, Contact, ExternalDocs, License, Tag};
use crate::runtime::lifecycle::{BoxError, BoxFuture};
use crate::runtime::shutdown::Shutdown;
pub(crate) type Starter<St> = Box<
dyn FnOnce(Arc<St>, Shutdown) -> BoxFuture<'static, Result<JoinHandle<()>, BoxError>> + Send,
>;
pub(crate) type StateInit<St> =
Box<dyn FnOnce() -> BoxFuture<'static, Result<St, BoxError>> + Send>;
pub(crate) type LifecycleHook<St> =
Box<dyn FnOnce(Arc<St>) -> BoxFuture<'static, Result<(), BoxError>> + Send>;
#[derive(Clone, Copy)]
enum LifecyclePhase {
AfterStartup,
OnShutdown,
AfterShutdown,
}
#[derive(Debug, Clone)]
#[non_exhaustive]
pub struct AppInfo {
pub title: String,
pub version: String,
pub description: Option<String>,
pub id: Option<AppId>,
pub terms_of_service: Option<String>,
pub contact: Contact,
pub license: Option<License>,
pub tags: Vec<Tag>,
pub external_docs: Option<ExternalDocs>,
}
impl AppInfo {
#[must_use]
pub fn new(title: impl Into<String>, version: impl Into<String>) -> Self {
Self {
title: title.into(),
version: version.into(),
description: None,
id: None,
terms_of_service: None,
contact: Contact::new(),
license: None,
tags: Vec::new(),
external_docs: None,
}
}
#[must_use]
pub fn description(mut self, description: impl Into<String>) -> Self {
self.description = Some(description.into());
self
}
#[must_use]
pub fn id(mut self, id: AppId) -> Self {
self.id = Some(id);
self
}
#[must_use]
pub fn terms_of_service(mut self, url: impl Into<String>) -> Self {
self.terms_of_service = Some(url.into());
self
}
#[must_use]
pub fn contact(mut self, contact: Contact) -> Self {
self.contact = contact;
self
}
#[must_use]
pub fn license(mut self, license: License) -> Self {
self.license = Some(license);
self
}
#[must_use]
pub fn tag(mut self, tag: Tag) -> Self {
self.tags.push(tag);
self
}
#[must_use]
pub fn external_docs(mut self, docs: ExternalDocs) -> Self {
self.external_docs = Some(docs);
self
}
}
#[derive(Debug, Error)]
#[non_exhaustive]
pub enum RustStreamError {
#[error("broker connect failed: {0}")]
Connect(#[source] BoxError),
#[error("startup hook failed: {0}")]
Startup(#[source] BoxError),
#[error("subscription failed: {0}")]
Subscribe(#[source] BoxError),
#[error("broker shutdown failed: {0}")]
Shutdown(#[source] BoxError),
#[error("dispatch task failed: {0}")]
Join(#[source] tokio::task::JoinError),
#[error("dispatch failed: {0}")]
Dispatch(String),
}
pub(super) mod lifecycle_hooks {
use std::{error::Error as StdError, future::Future, sync::Arc};
use crate::runtime::lifecycle::{BoxError, ConnectedSlot};
use crate::runtime::publish_source::pair_bound;
use crate::{Broker, Connected, PublishPolicy};
use super::LifecycleHook;
pub(crate) fn box_startup_publish<B, State, Source, Hook, Fut, E>(
slot: ConnectedSlot<B>,
source: Source,
hook: Hook,
) -> LifecycleHook<State>
where
B: Broker + 'static,
Source: PublishPolicy<Connected<B>> + Send + 'static,
Source::Live: Send,
Hook: FnOnce(Source::Live) -> Fut + Send + 'static,
Fut: Future<Output = Result<(), E>> + Send + 'static,
E: StdError + Send + Sync + 'static,
{
Box::new(move |_state: Arc<State>| {
Box::pin(async move {
let live = pair_bound::<B, Source>(&slot, source)
.await
.map_err(|e| Box::new(e) as BoxError)?;
hook(live).await.map_err(|e| Box::new(e) as BoxError)
})
})
}
}