cpex_core/extensions/
authorization.rs1use serde::{Deserialize, Serialize};
14use std::collections::BTreeMap;
15
16#[derive(Debug, Clone, Default, PartialEq, Serialize, Deserialize)]
22pub struct AuthorizationDetail {
23 #[serde(rename = "type")]
24 pub detail_type: String,
25
26 #[serde(default, skip_serializing_if = "Option::is_none")]
27 pub locations: Option<Vec<String>>,
28
29 #[serde(default, skip_serializing_if = "Option::is_none")]
30 pub actions: Option<Vec<String>>,
31
32 #[serde(default, skip_serializing_if = "Option::is_none")]
33 pub datatypes: Option<Vec<String>>,
34
35 #[serde(default, skip_serializing_if = "Option::is_none")]
36 pub identifier: Option<String>,
37
38 #[serde(default, skip_serializing_if = "Option::is_none")]
39 pub privileges: Option<Vec<String>>,
40
41 #[serde(flatten)]
44 pub extra: BTreeMap<String, serde_json::Value>,
45}
46
47#[cfg(test)]
48mod tests {
49 use super::*;
50
51 #[test]
52 fn serde_roundtrip_with_rfc9396_keyword() {
53 let detail = AuthorizationDetail {
54 detail_type: "tool_invocation".into(),
55 actions: Some(vec!["read".into()]),
56 identifier: Some("get_compensation".into()),
57 ..Default::default()
58 };
59 let json = serde_json::to_string(&detail).unwrap();
60 assert!(json.contains(r#""type":"tool_invocation""#));
62 assert!(!json.contains("detail_type"));
63
64 let back: AuthorizationDetail = serde_json::from_str(&json).unwrap();
65 assert_eq!(back, detail);
66 }
67
68 #[test]
69 fn extra_fields_round_trip() {
70 let json = r#"{
71 "type": "payment",
72 "actions": ["initiate"],
73 "amount": "100.00",
74 "currency": "USD"
75 }"#;
76 let detail: AuthorizationDetail = serde_json::from_str(json).unwrap();
77 assert_eq!(detail.detail_type, "payment");
78 assert_eq!(
79 detail.extra.get("amount").and_then(|v| v.as_str()),
80 Some("100.00")
81 );
82 assert_eq!(
83 detail.extra.get("currency").and_then(|v| v.as_str()),
84 Some("USD")
85 );
86 }
87}