1use super::annotations::Annotations;
6use super::types::{AudioContent, ImageContent, ResourceLink, TextContent};
7
8#[derive(Debug, Clone)]
10pub struct TextContentBuilder {
11 text: String,
12 annotations: Option<Annotations>,
13}
14
15impl TextContentBuilder {
16 pub fn new(text: impl Into<String>) -> Self {
18 Self {
19 text: text.into(),
20 annotations: None,
21 }
22 }
23
24 pub fn with_audience(mut self, audience: impl Into<String>) -> Self {
26 self.annotations
27 .get_or_insert_with(Annotations::new)
28 .audience
29 .get_or_insert_with(Vec::new)
30 .push(audience.into());
31 self
32 }
33
34 pub fn with_priority(mut self, priority: f64) -> Self {
36 self.annotations
37 .get_or_insert_with(Annotations::new)
38 .priority = Some(priority.clamp(0.0, 1.0));
39 self
40 }
41
42 pub fn with_last_modified(mut self, timestamp: impl Into<String>) -> Self {
44 self.annotations
45 .get_or_insert_with(Annotations::new)
46 .last_modified = Some(timestamp.into());
47 self
48 }
49
50 pub fn with_language(mut self, language: impl Into<String>) -> Self {
52 self.annotations
53 .get_or_insert_with(Annotations::new)
54 .language = Some(language.into());
55 self
56 }
57
58 pub fn build(self) -> TextContent {
60 TextContent {
61 r#type: "text".to_string(),
62 text: self.text,
63 annotations: self.annotations,
64 }
65 }
66}
67
68#[derive(Debug, Clone)]
70pub struct ImageContentBuilder {
71 data: String,
72 mime_type: String,
73 annotations: Option<Annotations>,
74}
75
76impl ImageContentBuilder {
77 pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
79 Self {
80 data: data.into(),
81 mime_type: mime_type.into(),
82 annotations: None,
83 }
84 }
85
86 pub fn with_audience(mut self, audience: impl Into<String>) -> Self {
88 self.annotations
89 .get_or_insert_with(Annotations::new)
90 .audience
91 .get_or_insert_with(Vec::new)
92 .push(audience.into());
93 self
94 }
95
96 pub fn with_priority(mut self, priority: f64) -> Self {
98 self.annotations
99 .get_or_insert_with(Annotations::new)
100 .priority = Some(priority.clamp(0.0, 1.0));
101 self
102 }
103
104 pub fn with_last_modified(mut self, timestamp: impl Into<String>) -> Self {
106 self.annotations
107 .get_or_insert_with(Annotations::new)
108 .last_modified = Some(timestamp.into());
109 self
110 }
111
112 pub fn with_language(mut self, language: impl Into<String>) -> Self {
114 self.annotations
115 .get_or_insert_with(Annotations::new)
116 .language = Some(language.into());
117 self
118 }
119
120 pub fn build(self) -> ImageContent {
122 ImageContent {
123 r#type: "image".to_string(),
124 data: self.data,
125 mime_type: self.mime_type,
126 annotations: self.annotations,
127 }
128 }
129}
130
131#[derive(Debug, Clone)]
133pub struct AudioContentBuilder {
134 data: String,
135 mime_type: String,
136 annotations: Option<Annotations>,
137}
138
139impl AudioContentBuilder {
140 pub fn new(data: impl Into<String>, mime_type: impl Into<String>) -> Self {
142 Self {
143 data: data.into(),
144 mime_type: mime_type.into(),
145 annotations: None,
146 }
147 }
148
149 pub fn with_audience(mut self, audience: impl Into<String>) -> Self {
151 self.annotations
152 .get_or_insert_with(Annotations::new)
153 .audience
154 .get_or_insert_with(Vec::new)
155 .push(audience.into());
156 self
157 }
158
159 pub fn with_priority(mut self, priority: f64) -> Self {
161 self.annotations
162 .get_or_insert_with(Annotations::new)
163 .priority = Some(priority.clamp(0.0, 1.0));
164 self
165 }
166
167 pub fn with_last_modified(mut self, timestamp: impl Into<String>) -> Self {
169 self.annotations
170 .get_or_insert_with(Annotations::new)
171 .last_modified = Some(timestamp.into());
172 self
173 }
174
175 pub fn with_language(mut self, language: impl Into<String>) -> Self {
177 self.annotations
178 .get_or_insert_with(Annotations::new)
179 .language = Some(language.into());
180 self
181 }
182
183 pub fn build(self) -> AudioContent {
185 AudioContent {
186 r#type: "audio".to_string(),
187 data: self.data,
188 mime_type: self.mime_type,
189 annotations: self.annotations,
190 }
191 }
192}
193
194#[derive(Debug, Clone)]
196pub struct ResourceLinkBuilder {
197 uri: String,
198 title: Option<String>,
199 annotations: Option<Annotations>,
200}
201
202impl ResourceLinkBuilder {
203 pub fn new(uri: impl Into<String>) -> Self {
205 Self {
206 uri: uri.into(),
207 title: None,
208 annotations: None,
209 }
210 }
211
212 pub fn with_title(mut self, title: impl Into<String>) -> Self {
214 self.title = Some(title.into());
215 self
216 }
217
218 pub fn with_audience(mut self, audience: impl Into<String>) -> Self {
220 self.annotations
221 .get_or_insert_with(Annotations::new)
222 .audience
223 .get_or_insert_with(Vec::new)
224 .push(audience.into());
225 self
226 }
227
228 pub fn with_priority(mut self, priority: f64) -> Self {
230 self.annotations
231 .get_or_insert_with(Annotations::new)
232 .priority = Some(priority.clamp(0.0, 1.0));
233 self
234 }
235
236 pub fn with_last_modified(mut self, timestamp: impl Into<String>) -> Self {
238 self.annotations
239 .get_or_insert_with(Annotations::new)
240 .last_modified = Some(timestamp.into());
241 self
242 }
243
244 pub fn with_language(mut self, language: impl Into<String>) -> Self {
246 self.annotations
247 .get_or_insert_with(Annotations::new)
248 .language = Some(language.into());
249 self
250 }
251
252 pub fn build(self) -> ResourceLink {
254 ResourceLink {
255 r#type: "resource".to_string(),
256 uri: self.uri,
257 title: self.title,
258 annotations: self.annotations,
259 }
260 }
261}
262
263pub fn text(text: impl Into<String>) -> TextContentBuilder {
265 TextContentBuilder::new(text)
266}
267
268pub fn image(data: impl Into<String>, mime_type: impl Into<String>) -> ImageContentBuilder {
270 ImageContentBuilder::new(data, mime_type)
271}
272
273pub fn audio(data: impl Into<String>, mime_type: impl Into<String>) -> AudioContentBuilder {
275 AudioContentBuilder::new(data, mime_type)
276}
277
278pub fn resource(uri: impl Into<String>) -> ResourceLinkBuilder {
280 ResourceLinkBuilder::new(uri)
281}
282
283#[cfg(test)]
284mod tests {
285 use super::*;
286
287 #[test]
288 fn test_text_builder() {
289 let content = text("Hello, world!")
290 .with_priority(0.9)
291 .with_audience("user")
292 .with_language("en")
293 .build();
294
295 assert_eq!(content.text, "Hello, world!");
296 assert!(content.annotations.is_some());
297
298 let ann = content.annotations.unwrap();
299 assert_eq!(ann.priority, Some(0.9));
300 assert_eq!(ann.audience, Some(vec!["user".to_string()]));
301 assert_eq!(ann.language, Some("en".to_string()));
302 }
303
304 #[test]
305 fn test_text_builder_multiple_audiences() {
306 let content = text("Important message")
307 .with_audience("user")
308 .with_audience("admin")
309 .build();
310
311 let ann = content.annotations.unwrap();
312 assert_eq!(
313 ann.audience,
314 Some(vec!["user".to_string(), "admin".to_string()])
315 );
316 }
317
318 #[test]
319 fn test_image_builder() {
320 let content = image("base64data", "image/png")
321 .with_priority(0.8)
322 .with_last_modified("2025-01-01T00:00:00Z")
323 .build();
324
325 assert_eq!(content.data, "base64data");
326 assert_eq!(content.mime_type, "image/png");
327
328 let ann = content.annotations.unwrap();
329 assert_eq!(ann.priority, Some(0.8));
330 assert_eq!(ann.last_modified, Some("2025-01-01T00:00:00Z".to_string()));
331 }
332
333 #[test]
334 fn test_audio_builder() {
335 let content = audio("audiodata", "audio/wav")
336 .with_priority(0.7)
337 .with_language("en")
338 .build();
339
340 assert_eq!(content.data, "audiodata");
341 assert_eq!(content.mime_type, "audio/wav");
342
343 let ann = content.annotations.unwrap();
344 assert_eq!(ann.priority, Some(0.7));
345 assert_eq!(ann.language, Some("en".to_string()));
346 }
347
348 #[test]
349 fn test_resource_builder() {
350 let link = resource("file://test.txt")
351 .with_title("Test File")
352 .with_priority(0.9)
353 .build();
354
355 assert_eq!(link.uri, "file://test.txt");
356 assert_eq!(link.title, Some("Test File".to_string()));
357
358 let ann = link.annotations.unwrap();
359 assert_eq!(ann.priority, Some(0.9));
360 }
361
362 #[test]
363 fn test_builder_without_annotations() {
364 let content = text("Plain text").build();
365
366 assert_eq!(content.text, "Plain text");
367 assert!(content.annotations.is_none());
368 }
369
370 #[test]
371 fn test_priority_clamping() {
372 let content1 = text("High").with_priority(1.5).build();
373 let content2 = text("Low").with_priority(-0.5).build();
374
375 assert_eq!(content1.annotations.unwrap().priority, Some(1.0));
376 assert_eq!(content2.annotations.unwrap().priority, Some(0.0));
377 }
378
379 #[test]
380 fn test_builder_type_usage() {
381 let builder = TextContentBuilder::new("test");
383 let content = builder.with_priority(0.5).build();
384
385 assert_eq!(content.text, "test");
386 assert_eq!(content.annotations.unwrap().priority, Some(0.5));
387 }
388}