use ::core::future::Future;
use ::core::time::Duration;
use std::collections::HashMap;
use anyhow::Context as _;
use async_nats::{ConnectOptions, Event};
use provider::ProviderInitState;
use tracing::{error, info, warn};
use wasmcloud_core::secrets::SecretValue;
pub mod error;
pub mod provider;
#[cfg(feature = "otel")]
pub mod otel;
pub use anyhow;
pub use provider::{
get_connection, load_host_data, run_provider, serve_provider_exports, ProviderConnection,
};
pub use tracing_subscriber;
pub use wasmcloud_core as core;
pub use wasmcloud_core::{
HealthCheckRequest, HealthCheckResponse, HostData, InterfaceLinkDefinition, WitFunction,
WitInterface, WitNamespace, WitPackage,
};
pub use wasmcloud_tracing;
pub fn parse_wit_meta_from_operation(
operation: impl AsRef<str>,
) -> anyhow::Result<(WitNamespace, WitPackage, WitInterface, Option<WitFunction>)> {
let operation = operation.as_ref();
let (ns_and_pkg, interface_and_func) = operation
.rsplit_once('/')
.context("failed to parse operation")?;
let (wit_iface, wit_fn) = interface_and_func
.split_once('.')
.context("interface and function should be specified")?;
let (wit_ns, wit_pkg) = ns_and_pkg
.rsplit_once(':')
.context("failed to parse operation for WIT ns/pkg")?;
Ok((
wit_ns.into(),
wit_pkg.into(),
wit_iface.into(),
if wit_fn.is_empty() {
None
} else {
Some(wit_fn.into())
},
))
}
pub const URL_SCHEME: &str = "wasmbus";
pub(crate) const DEFAULT_NATS_ADDR: &str = "nats://127.0.0.1:4222";
pub const DEFAULT_RPC_TIMEOUT_MILLIS: Duration = Duration::from_millis(2000);
#[must_use]
pub fn with_connection_event_logging(opts: ConnectOptions) -> ConnectOptions {
opts.event_callback(|event| async move {
match event {
Event::Connected => info!("nats client connected"),
Event::Disconnected => warn!("nats client disconnected"),
Event::Draining => warn!("nats client draining"),
Event::LameDuckMode => warn!("nats lame duck mode"),
Event::SlowConsumer(val) => warn!("nats slow consumer detected ({val})"),
Event::ClientError(err) => error!("nats client error: '{err:?}'"),
Event::ServerError(err) => error!("nats server error: '{err:?}'"),
Event::Closed => error!("nats client closed"),
}
})
}
#[derive(Default, Debug, Clone)]
pub struct Context {
pub component: Option<String>,
pub tracing: HashMap<String, String>,
}
impl Context {
#[must_use]
pub fn link_name(&self) -> &str {
self.tracing
.get("link-name")
.map_or("default", String::as_str)
}
}
#[non_exhaustive]
pub struct LinkConfig<'a> {
pub target_id: &'a str,
pub source_id: &'a str,
pub link_name: &'a str,
pub config: &'a HashMap<String, String>,
pub secrets: &'a HashMap<String, SecretValue>,
pub wit_metadata: (&'a WitNamespace, &'a WitPackage, &'a Vec<WitInterface>),
}
pub trait ProviderInitConfig: Send + Sync {
fn get_provider_id(&self) -> &str;
fn get_config(&self) -> &HashMap<String, String>;
fn get_secrets(&self) -> &HashMap<String, SecretValue>;
}
impl ProviderInitConfig for &ProviderInitState {
fn get_provider_id(&self) -> &str {
&self.provider_key
}
fn get_config(&self) -> &HashMap<String, String> {
&self.config
}
fn get_secrets(&self) -> &HashMap<String, SecretValue> {
&self.secrets
}
}
pub trait ProviderConfigUpdate: Send + Sync {
fn get_values(&self) -> &HashMap<String, String>;
}
impl ProviderConfigUpdate for &HashMap<String, String> {
fn get_values(&self) -> &HashMap<String, String> {
self
}
}
pub trait LinkDeleteInfo: Send + Sync {
fn get_source_id(&self) -> &str;
fn get_target_id(&self) -> &str;
fn get_link_name(&self) -> &str;
}
impl LinkDeleteInfo for &InterfaceLinkDefinition {
fn get_source_id(&self) -> &str {
&self.source_id
}
fn get_target_id(&self) -> &str {
&self.target
}
fn get_link_name(&self) -> &str {
&self.name
}
}
pub trait Provider<E = anyhow::Error>: Sync {
fn init(
&self,
init_config: impl ProviderInitConfig,
) -> impl Future<Output = Result<(), E>> + Send {
let _ = init_config;
async { Ok(()) }
}
fn on_config_update(
&self,
update: impl ProviderConfigUpdate,
) -> impl Future<Output = Result<(), E>> + Send {
let _ = update;
async { Ok(()) }
}
fn receive_link_config_as_source(
&self,
config: LinkConfig<'_>,
) -> impl Future<Output = Result<(), E>> + Send {
let _ = config;
async { Ok(()) }
}
fn receive_link_config_as_target(
&self,
config: LinkConfig<'_>,
) -> impl Future<Output = Result<(), E>> + Send {
let _ = config;
async { Ok(()) }
}
fn delete_link_as_target(
&self,
_info: impl LinkDeleteInfo,
) -> impl Future<Output = Result<(), E>> + Send {
async { Ok(()) }
}
fn delete_link_as_source(
&self,
_info: impl LinkDeleteInfo,
) -> impl Future<Output = Result<(), E>> + Send {
async { Ok(()) }
}
fn health_request(
&self,
_arg: &HealthCheckRequest,
) -> impl Future<Output = Result<HealthCheckResponse, E>> + Send {
async {
Ok(HealthCheckResponse {
healthy: true,
message: None,
})
}
}
fn shutdown(&self) -> impl Future<Output = Result<(), E>> + Send {
async { Ok(()) }
}
}