use crate::protocol::ProtocolKind;
use serde::{Deserialize, Serialize};
use std::collections::BTreeSet;
pub struct FieldEntry {
pub name: &'static str,
pub ty: &'static str,
}
pub struct ProtocolRegistration {
pub name: &'static str,
pub kind: ProtocolKind,
pub fields: &'static [FieldEntry],
pub doc: &'static str,
pub surfaces: &'static [&'static str],
}
inventory::collect!(ProtocolRegistration);
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ManifestField {
pub name: String,
pub ty: String,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ManifestEntry {
pub name: String,
pub kind: ProtocolKind,
pub fields: Vec<ManifestField>,
#[serde(skip_serializing_if = "String::is_empty")]
pub doc: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub surfaces: Vec<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct ProtocolManifest {
pub manifest_version: String,
pub protocol_version: String,
#[serde(
default = "ProtocolManifest::default_surface_name_owned",
skip_serializing_if = "ProtocolManifest::is_default_surface_name"
)]
pub default_surface: String,
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub surfaces: Vec<String>,
pub commands: Vec<ManifestEntry>,
pub queries: Vec<ManifestEntry>,
pub events: Vec<ManifestEntry>,
pub dtos: Vec<ManifestEntry>,
}
impl ProtocolManifest {
pub const MANIFEST_VERSION: &'static str = "2";
pub const DEFAULT_SURFACE: &'static str = "default";
fn default_surface_name_owned() -> String {
Self::DEFAULT_SURFACE.to_string()
}
fn is_default_surface_name(name: &String) -> bool {
name == Self::DEFAULT_SURFACE
}
pub fn collect(protocol_version: &str) -> Self {
Self::collect_with_default_surface(protocol_version, Self::DEFAULT_SURFACE)
}
pub fn collect_with_default_surface(protocol_version: &str, default_surface: &str) -> Self {
let mut commands = Vec::new();
let mut queries = Vec::new();
let mut events = Vec::new();
let mut dtos = Vec::new();
let mut surface_names = BTreeSet::new();
for reg in inventory::iter::<ProtocolRegistration> {
let mut surfaces: Vec<String> = reg
.surfaces
.iter()
.map(|surface| surface.to_string())
.collect();
surfaces.sort();
surfaces.dedup();
let resolved_surfaces = if surfaces.is_empty() {
vec![default_surface.to_string()]
} else {
surfaces.clone()
};
surface_names.extend(resolved_surfaces);
let entry = ManifestEntry {
name: reg.name.to_string(),
kind: reg.kind,
fields: reg
.fields
.iter()
.map(|f| ManifestField {
name: f.name.to_string(),
ty: f.ty.to_string(),
})
.collect(),
doc: reg.doc.to_string(),
surfaces,
};
match reg.kind {
ProtocolKind::Command => commands.push(entry),
ProtocolKind::Query => queries.push(entry),
ProtocolKind::Event => events.push(entry),
ProtocolKind::Dto => dtos.push(entry),
}
}
commands.sort_by(|a, b| a.name.cmp(&b.name));
queries.sort_by(|a, b| a.name.cmp(&b.name));
events.sort_by(|a, b| a.name.cmp(&b.name));
dtos.sort_by(|a, b| a.name.cmp(&b.name));
ProtocolManifest {
manifest_version: Self::MANIFEST_VERSION.to_string(),
protocol_version: protocol_version.to_string(),
default_surface: default_surface.to_string(),
surfaces: surface_names.into_iter().collect(),
commands,
queries,
events,
dtos,
}
}
pub fn entry_belongs_to_surface(
entry: &ManifestEntry,
surface: &str,
default_surface: &str,
) -> bool {
if entry.surfaces.is_empty() {
surface == default_surface
} else {
entry.surfaces.iter().any(|candidate| candidate == surface)
}
}
pub fn resolved_surface_names(&self) -> Vec<String> {
if !self.surfaces.is_empty() {
return self.surfaces.clone();
}
let mut surface_names = BTreeSet::new();
for entry in self
.commands
.iter()
.chain(self.queries.iter())
.chain(self.events.iter())
.chain(self.dtos.iter())
{
if entry.surfaces.is_empty() {
surface_names.insert(self.default_surface.clone());
} else {
surface_names.extend(entry.surfaces.iter().cloned());
}
}
if surface_names.is_empty() {
surface_names.insert(self.default_surface.clone());
}
surface_names.into_iter().collect()
}
pub fn to_json_pretty(&self) -> Result<String, serde_json::Error> {
serde_json::to_string_pretty(self)
}
pub fn to_ron_pretty(&self) -> Result<String, ron::Error> {
ron::ser::to_string_pretty(self, ron::ser::PrettyConfig::default())
}
}