Skip to main content

awp_types/
capability.rs

1use serde::{Deserialize, Serialize};
2
3/// JSON-LD capability manifest describing what a site offers to agents.
4///
5/// The `@context` field is always `"https://schema.org"` and `@type` is `"WebAPI"`.
6#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
7pub struct CapabilityManifest {
8    #[serde(rename = "@context")]
9    pub context: String,
10    #[serde(rename = "@type")]
11    pub type_: String,
12    pub name: String,
13    pub description: String,
14    pub capabilities: Vec<CapabilityEntry>,
15}
16
17/// A single capability entry within a [`CapabilityManifest`].
18#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
19#[serde(rename_all = "camelCase")]
20pub struct CapabilityEntry {
21    pub name: String,
22    pub description: String,
23    pub endpoint: String,
24    pub method: String,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub input_schema: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub output_schema: Option<String>,
29}
30
31#[cfg(test)]
32mod tests {
33    use super::*;
34
35    #[test]
36    fn test_serde_round_trip() {
37        let manifest = CapabilityManifest {
38            context: "https://schema.org".to_string(),
39            type_: "WebAPI".to_string(),
40            name: "Test API".to_string(),
41            description: "A test API".to_string(),
42            capabilities: vec![CapabilityEntry {
43                name: "get_data".to_string(),
44                description: "Get data".to_string(),
45                endpoint: "/api/data".to_string(),
46                method: "GET".to_string(),
47                input_schema: None,
48                output_schema: Some("DataResponse".to_string()),
49            }],
50        };
51        let json = serde_json::to_string(&manifest).unwrap();
52        let deserialized: CapabilityManifest = serde_json::from_str(&json).unwrap();
53        assert_eq!(manifest, deserialized);
54    }
55
56    #[test]
57    fn test_json_ld_fields() {
58        let manifest = CapabilityManifest {
59            context: "https://schema.org".to_string(),
60            type_: "WebAPI".to_string(),
61            name: "Test".to_string(),
62            description: "Test".to_string(),
63            capabilities: vec![],
64        };
65        let json = serde_json::to_string(&manifest).unwrap();
66        assert!(json.contains("\"@context\""));
67        assert!(json.contains("\"@type\""));
68    }
69
70    #[test]
71    fn test_optional_fields_skipped() {
72        let entry = CapabilityEntry {
73            name: "test".to_string(),
74            description: "test".to_string(),
75            endpoint: "/test".to_string(),
76            method: "GET".to_string(),
77            input_schema: None,
78            output_schema: None,
79        };
80        let json = serde_json::to_string(&entry).unwrap();
81        assert!(!json.contains("inputSchema"));
82        assert!(!json.contains("outputSchema"));
83    }
84}