use std::collections::BTreeMap;
use serde::{Deserialize, Serialize};
use serde_json::Value;
fn default_configuration() -> Value {
Value::Object(serde_json::Map::new())
}
#[derive(Clone, Debug, Default, Deserialize, PartialEq, Serialize)]
pub struct CapabilityEndpoint {
capability_id: String,
descriptor_version: String,
operations: Vec<String>,
#[serde(default)]
operation_kinds: BTreeMap<String, InteractionKind>,
#[serde(default, skip_serializing_if = "Option::is_none")]
admission: Option<RequestAdmission>,
#[serde(default)]
operation_admissions: BTreeMap<String, RequestAdmission>,
#[serde(default, skip_serializing_if = "Option::is_none")]
event_capacity: Option<usize>,
}
impl CapabilityEndpoint {
pub fn request(
capability_id: impl Into<String>,
descriptor_version: impl Into<String>,
operations: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
Self {
capability_id: capability_id.into(),
descriptor_version: descriptor_version.into(),
operations: operations.into_iter().map(Into::into).collect(),
..Self::default()
}
}
pub fn stream(
capability_id: impl Into<String>,
descriptor_version: impl Into<String>,
operations: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
let mut endpoint = Self::request(capability_id, descriptor_version, operations);
for operation in endpoint.operations.clone() {
endpoint
.operation_kinds
.insert(operation, InteractionKind::Stream);
}
endpoint
}
pub fn event(
capability_id: impl Into<String>,
descriptor_version: impl Into<String>,
operations: impl IntoIterator<Item = impl Into<String>>,
) -> Self {
let mut endpoint = Self::request(capability_id, descriptor_version, operations);
for operation in endpoint.operations.clone() {
endpoint
.operation_kinds
.insert(operation, InteractionKind::Event);
}
endpoint
}
#[must_use]
pub fn with_operation_kind(
mut self,
operation: impl Into<String>,
kind: InteractionKind,
) -> Self {
self.operation_kinds.insert(operation.into(), kind);
self
}
#[must_use]
pub fn with_admission(mut self, admission: RequestAdmission) -> Self {
self.admission = Some(admission);
self
}
#[must_use]
pub fn with_event_capacity(mut self, capacity: usize) -> Self {
self.event_capacity = Some(capacity);
self
}
pub fn capability_id(&self) -> &str {
&self.capability_id
}
pub fn descriptor_version(&self) -> &str {
&self.descriptor_version
}
pub fn operations(&self) -> &[String] {
&self.operations
}
pub fn operation_kinds(&self) -> &BTreeMap<String, InteractionKind> {
&self.operation_kinds
}
pub const fn admission(&self) -> Option<RequestAdmission> {
self.admission
}
pub fn operation_admissions(&self) -> &BTreeMap<String, RequestAdmission> {
&self.operation_admissions
}
pub const fn event_capacity(&self) -> Option<usize> {
self.event_capacity
}
}
#[derive(Clone, Copy, Debug, Default, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum InteractionKind {
#[default]
Request,
Stream,
Event,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
pub struct RequestAdmission {
queue_capacity: usize,
max_concurrency: usize,
}
impl RequestAdmission {
pub const fn new(queue_capacity: usize, max_concurrency: usize) -> Self {
Self {
queue_capacity,
max_concurrency,
}
}
pub const fn queue_capacity(self) -> usize {
self.queue_capacity
}
pub const fn max_concurrency(self) -> usize {
self.max_concurrency
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct CapabilityRequirement {
capability_id: String,
descriptor_version: String,
cardinality: Cardinality,
}
impl CapabilityRequirement {
pub fn one(capability_id: impl Into<String>, descriptor_version: impl Into<String>) -> Self {
Self::new(capability_id, descriptor_version, Cardinality::One)
}
pub fn optional(
capability_id: impl Into<String>,
descriptor_version: impl Into<String>,
) -> Self {
Self::new(capability_id, descriptor_version, Cardinality::Optional)
}
pub fn many(capability_id: impl Into<String>, descriptor_version: impl Into<String>) -> Self {
Self::new(capability_id, descriptor_version, Cardinality::Many)
}
pub fn new(
capability_id: impl Into<String>,
descriptor_version: impl Into<String>,
cardinality: Cardinality,
) -> Self {
Self {
capability_id: capability_id.into(),
descriptor_version: descriptor_version.into(),
cardinality,
}
}
pub fn capability_id(&self) -> &str {
&self.capability_id
}
pub fn descriptor_version(&self) -> &str {
&self.descriptor_version
}
pub const fn cardinality(&self) -> Cardinality {
self.cardinality
}
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Cardinality {
One,
Optional,
Many,
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Binding {
consumer: String,
capability_id: String,
descriptor_version: String,
provider: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
admission: Option<RequestAdmission>,
#[serde(default, skip_serializing_if = "Option::is_none")]
event_capacity: Option<usize>,
}
impl Binding {
pub fn new(
consumer: impl Into<String>,
capability_id: impl Into<String>,
descriptor_version: impl Into<String>,
provider: impl Into<String>,
) -> Self {
Self {
consumer: consumer.into(),
capability_id: capability_id.into(),
descriptor_version: descriptor_version.into(),
provider: provider.into(),
admission: None,
event_capacity: None,
}
}
#[must_use]
pub fn with_admission(mut self, admission: RequestAdmission) -> Self {
self.admission = Some(admission);
self
}
#[must_use]
pub fn with_event_capacity(mut self, capacity: usize) -> Self {
self.event_capacity = Some(capacity);
self
}
pub fn consumer(&self) -> &str {
&self.consumer
}
pub fn capability_id(&self) -> &str {
&self.capability_id
}
pub fn descriptor_version(&self) -> &str {
&self.descriptor_version
}
pub fn provider(&self) -> &str {
&self.provider
}
pub const fn admission(&self) -> Option<RequestAdmission> {
self.admission
}
pub const fn event_capacity(&self) -> Option<usize> {
self.event_capacity
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
pub struct Module {
key: String,
package: String,
#[serde(default = "default_entrypoint")]
entrypoint: String,
#[serde(default = "default_configuration")]
configuration: Value,
#[serde(default)]
provides: Vec<CapabilityEndpoint>,
#[serde(default)]
requires: Vec<CapabilityRequirement>,
#[serde(default, skip_serializing_if = "Option::is_none")]
execution_class: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
configuration_schema: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
role: Option<ModuleRole>,
#[serde(default, skip_serializing_if = "Option::is_none")]
execution_lane: Option<String>,
}
#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq, Serialize)]
#[serde(rename_all = "snake_case")]
pub enum ModuleRole {
WebShell,
BrowserAdapter,
UiContribution,
}
fn default_entrypoint() -> String {
"default".to_owned()
}
impl Module {
pub fn new(key: impl Into<String>, package: impl Into<String>) -> Self {
Self {
key: key.into(),
package: package.into(),
entrypoint: default_entrypoint(),
configuration: default_configuration(),
provides: Vec::new(),
requires: Vec::new(),
execution_class: None,
configuration_schema: None,
role: None,
execution_lane: None,
}
}
#[must_use]
pub fn with_entrypoint(mut self, entrypoint: impl Into<String>) -> Self {
self.entrypoint = entrypoint.into();
self
}
#[must_use]
pub fn with_configuration(mut self, configuration: Value) -> Self {
self.configuration = configuration;
self
}
pub fn set_configuration(&mut self, configuration: Value) {
self.configuration = configuration;
}
#[must_use]
pub fn with_configuration_schema(mut self, path: impl Into<String>) -> Self {
self.configuration_schema = Some(path.into());
self
}
#[must_use]
pub const fn with_role(mut self, role: ModuleRole) -> Self {
self.role = Some(role);
self
}
#[must_use]
pub fn with_execution_lane(mut self, execution_lane: impl Into<String>) -> Self {
self.execution_lane = Some(execution_lane.into());
self
}
#[must_use]
pub fn with_capability(mut self, capability: CapabilityEndpoint) -> Self {
self.provides.push(capability);
self
}
#[must_use]
pub fn with_requirement(mut self, requirement: CapabilityRequirement) -> Self {
self.requires.push(requirement);
self
}
pub fn set_execution_class(&mut self, execution_class: impl Into<String>) {
self.execution_class = Some(execution_class.into());
}
pub fn key(&self) -> &str {
&self.key
}
pub fn package(&self) -> &str {
&self.package
}
pub fn entrypoint(&self) -> &str {
&self.entrypoint
}
pub fn configuration(&self) -> &Value {
&self.configuration
}
pub fn provides(&self) -> &[CapabilityEndpoint] {
&self.provides
}
pub fn requires(&self) -> &[CapabilityRequirement] {
&self.requires
}
pub fn execution_class(&self) -> Option<&str> {
self.execution_class.as_deref()
}
pub fn configuration_schema(&self) -> Option<&str> {
self.configuration_schema.as_deref()
}
pub const fn role(&self) -> Option<ModuleRole> {
self.role
}
pub fn execution_lane(&self) -> Option<&str> {
self.execution_lane.as_deref()
}
}