use std::borrow::Cow;
use std::fmt;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::{Map, Value};
use crate::capability::{Capability, CAPABILITIES};
use crate::schema::{Message, SchemaId};
pub const RESIDENT_PROTOCOL: SchemaId = SchemaId::literal("bus", "resident-protocol", 1);
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
#[schemars(title = "ResidentLine")]
pub enum ResidentLine {
Request(ResidentRequest),
Cancel(ResidentCancel),
Answer(ResidentAnswer),
Failure(ResidentFailure),
Event(ResidentEvent),
}
impl Message for ResidentLine {
const SCHEMA: SchemaId = RESIDENT_PROTOCOL;
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, Serialize, Deserialize)]
#[serde(transparent)]
pub struct RequestId(pub u64);
impl fmt::Display for RequestId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
self.0.fmt(f)
}
}
impl JsonSchema for RequestId {
fn schema_name() -> Cow<'static, str> {
u64::schema_name()
}
fn schema_id() -> Cow<'static, str> {
u64::schema_id()
}
fn inline_schema() -> bool {
u64::inline_schema()
}
fn json_schema(generator: &mut schemars::SchemaGenerator) -> schemars::Schema {
u64::json_schema(generator)
}
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResidentRequest {
pub id: RequestId,
pub verb: ResidentVerb,
#[serde(default, skip_serializing_if = "Map::is_empty")]
pub args: Map<String, Value>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub input: Option<String>,
}
#[derive(Debug, Clone, Copy)]
pub struct ResidentVerb(&'static Capability);
impl ResidentVerb {
#[must_use]
pub fn named(method: &str) -> Option<Self> {
CAPABILITIES
.iter()
.find(|capability| capability.method == method)
.map(Self)
}
#[must_use]
pub const fn capability(self) -> &'static Capability {
self.0
}
#[must_use]
pub const fn method(self) -> &'static str {
self.0.method
}
}
impl PartialEq for ResidentVerb {
fn eq(&self, other: &Self) -> bool {
self.method() == other.method()
}
}
impl Eq for ResidentVerb {}
impl Serialize for ResidentVerb {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_str(self.method())
}
}
impl<'de> Deserialize<'de> for ResidentVerb {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
let method = String::deserialize(deserializer)?;
Self::named(&method).ok_or_else(|| {
serde::de::Error::custom(format!(
"`{method}` is not a verb of the resident core; it answers each capability's method: {}",
CAPABILITIES
.iter()
.map(|capability| capability.method)
.collect::<Vec<_>>()
.join(", ")
))
})
}
}
impl JsonSchema for ResidentVerb {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("ResidentVerb")
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
let methods: Vec<&str> = CAPABILITIES
.iter()
.map(|capability| capability.method)
.collect();
schemars::json_schema!({
"type": "string",
"enum": methods,
"description": "A capability's SDK method, camelCase, as the capability manifest names it."
})
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResidentCancel {
pub id: RequestId,
pub cancel: True,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResidentAnswer {
pub id: RequestId,
pub ok: Value,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResidentFailure {
pub id: Option<RequestId>,
pub error: ResidentRefusal,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResidentRefusal {
pub exit: ResidentExit,
pub message: String,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub output: Option<Value>,
}
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ResidentEvent {
pub id: RequestId,
pub event: Value,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum ResidentExit {
Failed,
Invalid,
}
impl ResidentExit {
#[must_use]
pub const fn code(self) -> u8 {
match self {
Self::Failed => 1,
Self::Invalid => 2,
}
}
}
impl Serialize for ResidentExit {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_u8(self.code())
}
}
impl<'de> Deserialize<'de> for ResidentExit {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
match u8::deserialize(deserializer)? {
1 => Ok(Self::Failed),
2 => Ok(Self::Invalid),
other => Err(serde::de::Error::custom(format!(
"{other} is not an exit code a refusal carries; it is 1 or 2"
))),
}
}
}
impl JsonSchema for ResidentExit {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("ResidentExit")
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "integer",
"enum": [1, 2],
"description": "1: well-formed input whose answer is no; 2: input the verb refuses."
})
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct True;
impl Serialize for True {
fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
serializer.serialize_bool(true)
}
}
impl<'de> Deserialize<'de> for True {
fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
if bool::deserialize(deserializer)? {
Ok(Self)
} else {
Err(serde::de::Error::custom(
"a cancel line's `cancel` is `true`; leave the request running by sending nothing",
))
}
}
}
impl JsonSchema for True {
fn schema_name() -> Cow<'static, str> {
Cow::Borrowed("True")
}
fn inline_schema() -> bool {
true
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({"const": true})
}
}