1use crate::capabilities::CapabilitiesDocument;
15
16#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
19pub enum Profile {
20 RegistryCore,
22 RegistryDiscovery,
24 RegistryFederated,
26 RegistryReceipts,
29 RegistryHeadReceipts,
34 Consumer,
36}
37
38impl Profile {
39 pub fn as_str(self) -> &'static str {
42 match self {
43 Profile::RegistryCore => "acdp-registry-core",
44 Profile::RegistryDiscovery => "acdp-registry-discovery",
45 Profile::RegistryFederated => "acdp-registry-federated",
46 Profile::RegistryReceipts => "acdp-registry-receipts",
47 Profile::RegistryHeadReceipts => "acdp-registry-head-receipts",
48 Profile::Consumer => "acdp-consumer",
49 }
50 }
51}
52
53pub const CLAIMED: &[Profile] = &[Profile::Consumer];
59
60impl CapabilitiesDocument {
61 pub fn claims_profile(&self, profile: Profile) -> bool {
63 self.profiles.iter().any(|p| p == profile.as_str())
64 }
65
66 pub fn supports_required(
70 &self,
71 required: &[Profile],
72 ) -> Result<(), acdp_primitives::error::AcdpError> {
73 for p in required {
74 if !self.claims_profile(*p) {
75 return Err(acdp_primitives::error::AcdpError::SchemaViolation(format!(
76 "registry does not advertise required profile '{}'",
77 p.as_str()
78 )));
79 }
80 }
81 Ok(())
82 }
83}
84
85#[cfg(test)]
86mod tests {
87 use super::*;
88 use crate::capabilities::Limits;
89
90 fn caps_with(profiles: Vec<&str>) -> CapabilitiesDocument {
91 CapabilitiesDocument {
92 acdp_version: "0.1.0".into(),
93 registry_did: "did:web:r.example.com".into(),
94 supported_signature_algorithms: vec!["ed25519".into()],
95 supported_did_methods: vec!["did:web".into()],
96 profiles: profiles.into_iter().map(String::from).collect(),
97 limits: Limits {
98 max_payload_bytes: 1_048_576,
99 max_embedded_bytes: 65_536,
100 idempotency_key_ttl_seconds: None,
101 max_publish_per_minute: None,
102 },
103 read_authentication_methods: vec![],
104 anonymous_public_reads: true,
105 supports_idempotency_key: false,
106 extensions: Default::default(),
107 }
108 }
109
110 #[test]
111 fn claimed_profile_matches() {
112 let caps = caps_with(vec!["acdp-registry-core", "acdp-registry-discovery"]);
113 assert!(caps.claims_profile(Profile::RegistryCore));
114 assert!(caps.claims_profile(Profile::RegistryDiscovery));
115 assert!(!caps.claims_profile(Profile::RegistryFederated));
116 }
117
118 #[test]
119 fn supports_required_returns_first_missing() {
120 let caps = caps_with(vec!["acdp-registry-core"]);
121 caps.supports_required(&[Profile::RegistryCore]).unwrap();
122 let err = caps
123 .supports_required(&[Profile::RegistryCore, Profile::RegistryFederated])
124 .unwrap_err();
125 match err {
126 acdp_primitives::error::AcdpError::SchemaViolation(msg) => {
127 assert!(msg.contains("acdp-registry-federated"));
128 }
129 other => panic!("got {other:?}"),
130 }
131 }
132
133 #[test]
134 fn claimed_includes_consumer() {
135 assert!(CLAIMED.contains(&Profile::Consumer));
136 }
137}