///|
/// The opaque scope carried by a contract route.
#doc(hidden)
pub enum ContractScope {
Application
Extension(id~ : String, js_namespace~ : String)
}
///|
/// A request payload with no fields, shared by commands that take no input.
pub(all) struct EmptyRequest {} derive(ToJson, FromJson, Debug, Eq)
///|
pub extend EmptyRequest with Eq::{not_equal, equal}
///|
pub extend EmptyRequest with Debug::{to_repr}
///|
pub extend EmptyRequest with ToJson::{to_json}
///|
pub extend EmptyRequest with FromJson::{from_json}
///|
/// Standard platform-support probe result, shared by extension `support` commands.
pub(all) struct SupportReply {
supported : Bool
platform : String
reason : String?
} derive(ToJson, FromJson, Debug, Eq)
///|
pub extend SupportReply with Eq::{not_equal, equal}
///|
pub extend SupportReply with Debug::{to_repr}
///|
pub extend SupportReply with ToJson::{to_json}
///|
pub extend SupportReply with FromJson::{from_json}
///|
/// An opaque application or extension route carried by a typed descriptor.
///
/// Application code does not construct routes or assemble transport names
/// directly. Use `command`, `event`, or an `ExtensionContract`.
pub struct ContractRoute {
scope : ContractScope
name : String
}
///|
/// A target-neutral command descriptor.
///
/// The request and response parameters bind one stable command identity to its
/// payload types without storing serialization logic.
pub struct Command[Request, Response] {
route : ContractRoute
type_marker : ((Request) -> Response)?
}
///|
/// A target-neutral live-event descriptor.
///
/// Events are not replayed by the descriptor. Runtime integrations decide how
/// an event is emitted and subscribed. Descriptors are inert values; share
/// their definitions across processes to keep payload types consistent.
pub struct Event[Payload] {
route : ContractRoute
type_marker : ((Payload) -> Unit)?
}
///|
/// The identity and route scope shared by one Proton extension contract.
pub struct ExtensionContract {
id : String
js_namespace : String
}
///|
/// Failures found while validating a contract descriptor before use.
pub(all) suberror ContractDefinitionError {
EmptyApplicationName
EmptyExtensionId
EmptyExtensionNamespace
EmptyExtensionMember(extension_namespace~ : String)
ExpectedExtensionEvent(route~ : String)
} derive(Debug, Eq)
///|
pub extend ContractDefinitionError with Eq::{not_equal, equal}
///|
pub extend ContractDefinitionError with Debug::{to_repr}
///|
pub extend ContractDefinitionError with Show::{to_string, output}
///|
/// Creates an application command descriptor.
pub fn[Request, Response] command(name : String) -> Command[Request, Response] {
Command::{ route: application_route(name), type_marker: None, }
}
///|
/// Creates an inert application event descriptor.
///
/// Reconstructing a descriptor has no registration side effects. Runtime
/// values belong in the payload; export shared descriptors for stable routes.
pub fn[Payload] event(name : String) -> Event[Payload] {
new_event(application_route(name))
}
///|
/// Creates the identity shared by an extension's command and event descriptors.
///
/// Extension packages normally expose a value generated from `proton.ext.json`
/// instead of calling this constructor by hand.
pub fn extension(id~ : String, js_namespace~ : String) -> ExtensionContract {
ExtensionContract::{ id, js_namespace, }
}
///|
/// Creates a typed command descriptor scoped to this extension.
pub fn[Request, Response] ExtensionContract::command(
self : ExtensionContract,
name : String,
) -> Command[Request, Response] {
Command::{ route: self.route(name), type_marker: None, }
}
///|
/// Creates an inert typed live-event descriptor scoped to this extension.
///
/// Descriptor construction does not register a listener or event source.
pub fn[Payload] ExtensionContract::event(
self : ExtensionContract,
name : String,
) -> Event[Payload] {
new_event(self.route(name))
}
///|
/// Returns this extension contract's stable catalog identity.
pub fn ExtensionContract::id(self : ExtensionContract) -> String {
self.id
}
///|
/// Returns this extension contract's JavaScript and transport namespace.
pub fn ExtensionContract::js_namespace(self : ExtensionContract) -> String {
self.js_namespace
}
///|
/// Returns a command's stable member name without a transport prefix.
pub fn[Request, Response] Command::name(
self : Command[Request, Response],
) -> String {
self.route.name
}
///|
/// Returns an event's stable member name without a transport prefix.
pub fn[Payload] Event::name(self : Event[Payload]) -> String {
self.route.name
}
///|
/// Validates a command descriptor before registration or invocation.
pub fn[Request, Response] Command::validate(
self : Command[Request, Response],
) -> Unit raise ContractDefinitionError {
self.route.validate()
}
///|
/// Validates an event descriptor before subscription or emission.
pub fn[Payload] Event::validate(
self : Event[Payload],
) -> Unit raise ContractDefinitionError {
self.route.validate()
}
///|
/// Validates the extension identity before it is installed or used.
pub fn ExtensionContract::validate(
self : ExtensionContract,
) -> Unit raise ContractDefinitionError {
guard self.id.trim().to_owned() != "" else { raise EmptyExtensionId }
guard self.js_namespace.trim().to_owned() != "" else {
raise EmptyExtensionNamespace
}
}
///|
/// Returns the opaque route used by framework integrations.
#doc(hidden)
pub fn[Request, Response] Command::contract_route(
self : Command[Request, Response],
) -> ContractRoute {
self.route
}
///|
/// Returns the opaque route used by framework integrations.
#doc(hidden)
pub fn[Payload] Event::contract_route(self : Event[Payload]) -> ContractRoute {
self.route
}
///|
/// Returns the internal operation name consumed by the bridge transport.
#doc(hidden)
pub fn ContractRoute::operation_name(self : ContractRoute) -> String {
match self.scope {
Application => "app:" + self.name
Extension(js_namespace~, ..) => "ext:" + js_namespace + "/" + self.name
}
}
///|
/// Returns the stable member name carried by this route.
#doc(hidden)
pub fn ContractRoute::member_name(self : ContractRoute) -> String {
self.name
}
///|
/// Returns whether this route belongs to the application contract.
#doc(hidden)
pub fn ContractRoute::is_application(self : ContractRoute) -> Bool {
self.scope is Application
}
///|
/// Returns the extension catalog identity for an extension route.
#doc(hidden)
pub fn ContractRoute::extension_id(self : ContractRoute) -> String? {
match self.scope {
Application => None
Extension(id~, ..) => Some(id)
}
}
///|
/// Returns the extension namespace for an extension route.
#doc(hidden)
pub fn ContractRoute::extension_namespace(self : ContractRoute) -> String? {
match self.scope {
Application => None
Extension(js_namespace~, ..) => Some(js_namespace)
}
}
///|
fn application_route(name : String) -> ContractRoute {
ContractRoute::{ scope: Application, name, }
}
///|
fn[Payload] new_event(route : ContractRoute) -> Event[Payload] {
Event::{ route, type_marker: None, }
}
///|
fn ExtensionContract::route(
self : ExtensionContract,
name : String,
) -> ContractRoute {
ContractRoute::{
scope: Extension(id=self.id, js_namespace=self.js_namespace),
name,
}
}
///|
fn ContractRoute::validate(
self : ContractRoute,
) -> Unit raise ContractDefinitionError {
match self.scope {
Application => {
guard self.name.trim().to_owned() != "" else {
raise EmptyApplicationName
}
}
Extension(id~, js_namespace~) => {
guard id.trim().to_owned() != "" else { raise EmptyExtensionId }
guard js_namespace.trim().to_owned() != "" else {
raise EmptyExtensionNamespace
}
guard self.name.trim().to_owned() != "" else {
raise EmptyExtensionMember(extension_namespace=js_namespace)
}
}
}
}
///|
pub fn ContractDefinitionError::message(
self : ContractDefinitionError,
) -> String {
match self {
EmptyApplicationName => "application contract name must not be empty"
EmptyExtensionId => "extension contract id must not be empty"
EmptyExtensionNamespace => "extension contract namespace must not be empty"
EmptyExtensionMember(extension_namespace~) =>
"extension contract member must not be empty: " + extension_namespace
ExpectedExtensionEvent(route~) =>
"event route does not belong to an extension: " + route
}
}
///|
impl Show for ContractDefinitionError with fn output(self, logger) {
logger.write_string(self.message())
}