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