use serde::Serialize;
use crate::metadata::{ParticipantContract, ParticipantKind, ParticipantMetadata};
use crate::version::FrameworkVersion;
use crate::wire_schema::{DescribeWire, WireSchema};
#[doc(hidden)]
pub use const_format::concatcp;
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ParticipantContractRecord<'a> {
pub framework: FrameworkVersion,
pub id: &'a str,
pub kind: ParticipantKind,
pub config_schema: serde_json::Value,
}
#[derive(Clone, Debug, PartialEq, Serialize)]
#[serde(tag = "schema")]
pub enum ParticipantMetadataRecord<'a> {
#[serde(rename = "phoxal/participant-metadata/v0")]
V0 {
#[serde(flatten)]
contract: ParticipantContractRecord<'a>,
},
}
impl DescribeWire for ParticipantContractRecord<'_> {
fn wire_schema() -> WireSchema {
ParticipantContract::wire_schema()
}
}
impl DescribeWire for ParticipantMetadataRecord<'_> {
fn wire_schema() -> WireSchema {
ParticipantMetadata::wire_schema()
}
}
#[doc(hidden)]
#[macro_export]
macro_rules! participant_metadata_json {
(
framework = $framework:expr,
id = $id:expr,
kind = $kind:expr,
config_schema = $config_schema:expr $(,)?
) => {{
const __PHOXAL_FRAMEWORK: &str = $framework;
const __PHOXAL_KIND: &str = $kind.as_str();
$crate::emit::concatcp!(
"{\"schema\":\"phoxal/participant-metadata/v0\",\"framework\":\"",
__PHOXAL_FRAMEWORK,
"\",\"id\":\"",
$id,
"\",\"kind\":\"",
__PHOXAL_KIND,
"\",\"config_schema\":",
$config_schema,
"}"
)
}};
}
#[cfg(test)]
mod tests {
use super::*;
use crate::metadata::ParticipantMetadata;
const CONFIG_SCHEMA: &str = r#"{"type":"null"}"#;
const EMBEDDED: &str = participant_metadata_json!(
framework = FrameworkVersion::CURRENT_SPELLING,
id = "drive",
kind = ParticipantKind::Service,
config_schema = CONFIG_SCHEMA,
);
fn typed_record() -> ParticipantMetadataRecord<'static> {
ParticipantMetadataRecord::V0 {
contract: ParticipantContractRecord {
framework: FrameworkVersion::CURRENT,
id: "drive",
kind: ParticipantKind::Service,
config_schema: serde_json::json!({"type": "null"}),
},
}
}
#[test]
fn the_const_writer_emits_exactly_what_the_typed_record_serializes() {
let const_written: serde_json::Value =
serde_json::from_str(EMBEDDED).expect("the const writer emits a JSON document");
let typed = serde_json::to_value(typed_record()).expect("the typed record serializes");
assert_eq!(const_written, typed);
}
#[test]
fn the_const_written_bytes_have_the_declared_document_shape() {
let const_written: serde_json::Value =
serde_json::from_str(EMBEDDED).expect("the const writer emits a JSON document");
assert_eq!(
ParticipantMetadataRecord::wire_schema().conforms(&const_written),
Ok(())
);
}
#[test]
fn an_emitted_record_parses_back_into_the_typed_contract() {
let metadata = ParticipantMetadata::from_bytes(EMBEDDED.as_bytes())
.expect("the writer's own output must satisfy the parser");
let contract = metadata.contract();
assert_eq!(contract.framework, FrameworkVersion::CURRENT);
assert_eq!(contract.id.as_str(), "drive");
assert_eq!(contract.kind, ParticipantKind::Service);
assert_eq!(contract.config_schema, serde_json::json!({"type": "null"}));
}
#[test]
fn the_const_written_document_is_self_contained() {
assert!(
EMBEDDED.starts_with('{') && EMBEDDED.ends_with('}'),
"{EMBEDDED}"
);
assert_eq!(EMBEDDED.len(), EMBEDDED.trim().len());
}
}