1use serde::{Deserialize, Serialize};
2
3use super::{Annotated, Icon, Meta};
4
5#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
7#[serde(rename_all = "camelCase")]
8#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
9pub struct RawResource {
10 pub uri: String,
12 pub name: String,
14 #[serde(skip_serializing_if = "Option::is_none")]
16 pub title: Option<String>,
17 #[serde(skip_serializing_if = "Option::is_none")]
19 pub description: Option<String>,
20 #[serde(skip_serializing_if = "Option::is_none")]
22 pub mime_type: Option<String>,
23
24 #[serde(skip_serializing_if = "Option::is_none")]
28 pub size: Option<u32>,
29 #[serde(skip_serializing_if = "Option::is_none")]
31 pub icons: Option<Vec<Icon>>,
32 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
34 pub meta: Option<Meta>,
35}
36
37pub type Resource = Annotated<RawResource>;
38
39#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
40#[serde(rename_all = "camelCase")]
41#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
42pub struct RawResourceTemplate {
43 pub uri_template: String,
44 pub name: String,
45 #[serde(skip_serializing_if = "Option::is_none")]
46 pub title: Option<String>,
47 #[serde(skip_serializing_if = "Option::is_none")]
48 pub description: Option<String>,
49 #[serde(skip_serializing_if = "Option::is_none")]
50 pub mime_type: Option<String>,
51 #[serde(skip_serializing_if = "Option::is_none")]
53 pub icons: Option<Vec<Icon>>,
54}
55
56pub type ResourceTemplate = Annotated<RawResourceTemplate>;
57
58#[derive(Debug, Serialize, Deserialize, Clone, PartialEq)]
59#[serde(untagged)]
60#[cfg_attr(feature = "schemars", derive(schemars::JsonSchema))]
61pub enum ResourceContents {
62 #[serde(rename_all = "camelCase")]
63 TextResourceContents {
64 uri: String,
65 #[serde(skip_serializing_if = "Option::is_none")]
66 mime_type: Option<String>,
67 text: String,
68 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
69 meta: Option<Meta>,
70 },
71 #[serde(rename_all = "camelCase")]
72 BlobResourceContents {
73 uri: String,
74 #[serde(skip_serializing_if = "Option::is_none")]
75 mime_type: Option<String>,
76 blob: String,
77 #[serde(rename = "_meta", skip_serializing_if = "Option::is_none")]
78 meta: Option<Meta>,
79 },
80}
81
82impl ResourceContents {
83 pub fn text(text: impl Into<String>, uri: impl Into<String>) -> Self {
84 Self::TextResourceContents {
85 uri: uri.into(),
86 mime_type: Some("text".into()),
87 text: text.into(),
88 meta: None,
89 }
90 }
91}
92
93impl RawResource {
94 pub fn new(uri: impl Into<String>, name: impl Into<String>) -> Self {
96 Self {
97 uri: uri.into(),
98 name: name.into(),
99 title: None,
100 description: None,
101 mime_type: None,
102 size: None,
103 icons: None,
104 meta: None,
105 }
106 }
107}
108
109#[cfg(test)]
110mod tests {
111 use serde_json;
112
113 use super::*;
114
115 #[test]
116 fn test_resource_serialization() {
117 let resource = RawResource {
118 uri: "file:///test.txt".to_string(),
119 title: None,
120 name: "test".to_string(),
121 description: Some("Test resource".to_string()),
122 mime_type: Some("text/plain".to_string()),
123 size: Some(100),
124 icons: None,
125 meta: None,
126 };
127
128 let json = serde_json::to_string(&resource).unwrap();
129 println!("Serialized JSON: {}", json);
130
131 assert!(json.contains("mimeType"));
133 assert!(!json.contains("mime_type"));
134 }
135
136 #[test]
137 fn test_resource_contents_serialization() {
138 let text_contents = ResourceContents::TextResourceContents {
139 uri: "file:///test.txt".to_string(),
140 mime_type: Some("text/plain".to_string()),
141 text: "Hello world".to_string(),
142 meta: None,
143 };
144
145 let json = serde_json::to_string(&text_contents).unwrap();
146 println!("ResourceContents JSON: {}", json);
147
148 assert!(json.contains("mimeType"));
150 assert!(!json.contains("mime_type"));
151 }
152
153 #[test]
154 fn test_resource_template_with_icons() {
155 let resource_template = RawResourceTemplate {
156 uri_template: "file:///{path}".to_string(),
157 name: "template".to_string(),
158 title: Some("Test Template".to_string()),
159 description: Some("A test resource template".to_string()),
160 mime_type: Some("text/plain".to_string()),
161 icons: Some(vec![Icon {
162 src: "https://example.com/icon.png".to_string(),
163 mime_type: Some("image/png".to_string()),
164 sizes: Some(vec!["48x48".to_string()]),
165 }]),
166 };
167
168 let json = serde_json::to_value(&resource_template).unwrap();
169 assert!(json["icons"].is_array());
170 assert_eq!(json["icons"][0]["src"], "https://example.com/icon.png");
171 assert_eq!(json["icons"][0]["sizes"][0], "48x48");
172 }
173
174 #[test]
175 fn test_resource_template_without_icons() {
176 let resource_template = RawResourceTemplate {
177 uri_template: "file:///{path}".to_string(),
178 name: "template".to_string(),
179 title: None,
180 description: None,
181 mime_type: None,
182 icons: None,
183 };
184
185 let json = serde_json::to_value(&resource_template).unwrap();
186 assert!(json.get("icons").is_none());
187 }
188}