phoxal_runtime_contract/
metadata.rs1use serde::Deserialize;
2
3pub const PARTICIPANT_METADATA_SCHEMA: &str = "phoxal/participant-metadata/v0";
5
6#[derive(Clone, Copy, Debug, Deserialize, Eq, PartialEq)]
7#[serde(rename_all = "snake_case")]
8pub enum ParticipantKind {
9 Service,
10 Driver,
11 Simulator,
12}
13
14#[derive(Clone, Debug, Deserialize, PartialEq)]
15#[serde(deny_unknown_fields)]
16pub struct ParticipantMetadata {
17 pub schema: String,
18 pub id: String,
19 pub kind: ParticipantKind,
20 pub config_schema: serde_json::Value,
21}
22
23pub fn parse_participant_metadata(bytes: &[u8]) -> Result<ParticipantMetadata, MetadataError> {
25 let metadata: ParticipantMetadata = serde_json::from_slice(bytes)?;
26 if metadata.schema != PARTICIPANT_METADATA_SCHEMA {
27 return Err(MetadataError::Schema(metadata.schema));
28 }
29 Ok(metadata)
30}
31
32#[derive(Debug, thiserror::Error)]
33pub enum MetadataError {
34 #[error("participant metadata JSON is invalid: {0}")]
35 Json(#[from] serde_json::Error),
36 #[error("unsupported participant metadata schema '{0}'")]
37 Schema(String),
38}
39
40#[cfg(test)]
41mod tests {
42 use super::*;
43
44 #[test]
45 fn strict_metadata_shape_round_trips() {
46 let bytes = br#"{"schema":"phoxal/participant-metadata/v0","id":"drive","kind":"service","config_schema":{"type":"null"}}"#;
47 let metadata = parse_participant_metadata(bytes).unwrap();
48 assert_eq!(metadata.kind, ParticipantKind::Service);
49 assert_eq!(metadata.id, "drive");
50 }
51
52 #[test]
53 fn unknown_schema_and_fields_are_rejected() {
54 assert!(matches!(
55 parse_participant_metadata(
56 br#"{"schema":"v1","id":"drive","kind":"service","config_schema":null}"#
57 ),
58 Err(MetadataError::Schema(_))
59 ));
60 assert!(parse_participant_metadata(br#"{"schema":"phoxal/participant-metadata/v0","id":"drive","kind":"service","config_schema":null,"extra":true}"#).is_err());
61 assert!(parse_participant_metadata(br#"{"schema":"phoxal/participant-metadata/v0","id":"drive","kind":"service","class":"checked","config_schema":null}"#).is_err());
64 }
65}