1use serde::{Deserialize, Serialize};
2
3use super::{Annotation, ResourceContents, Role};
4
5#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
6#[serde(rename_all = "camelCase")]
7pub struct TextContent {
8 pub text: String,
9 #[serde(skip_serializing_if = "Option::is_none")]
10 pub annotation: Option<Annotation>,
11}
12
13#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
14#[serde(rename_all = "camelCase")]
15pub struct ImageContent {
16 pub data: String,
17 pub mime_type: String,
18 #[serde(skip_serializing_if = "Option::is_none")]
19 pub annotation: Option<Annotation>,
20}
21
22#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
23#[serde(rename_all = "camelCase")]
24pub struct EmbeddedResource {
25 pub resource: ResourceContents,
26 #[serde(skip_serializing_if = "Option::is_none")]
27 pub annotation: Option<Annotation>,
28}
29
30impl EmbeddedResource {
31 pub fn get_text(&self) -> String {
32 match &self.resource {
33 ResourceContents::TextResourceContents { text, .. } => text.clone(),
34 _ => String::new(),
35 }
36 }
37}
38
39#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
40#[serde(tag = "type", rename_all = "camelCase")]
41pub enum Content {
42 Text(TextContent),
43 Image(ImageContent),
44 Resource(EmbeddedResource),
45}
46
47impl Content {
48 pub fn text<S: Into<String>>(text: S) -> Self {
49 Content::Text(TextContent {
50 text: text.into(),
51 annotation: None,
52 })
53 }
54
55 pub fn image<S: Into<String>, T: Into<String>>(data: S, mime_type: T) -> Self {
56 Content::Image(ImageContent {
57 data: data.into(),
58 mime_type: mime_type.into(),
59 annotation: None,
60 })
61 }
62
63 pub fn resource(resource: ResourceContents) -> Self {
64 Content::Resource(EmbeddedResource {
65 resource,
66 annotation: None,
67 })
68 }
69
70 pub fn embedded_text<S: Into<String>, T: Into<String>>(uri: S, content: T) -> Self {
71 Content::Resource(EmbeddedResource {
72 resource: ResourceContents::TextResourceContents {
73 uri: uri.into(),
74 mime_type: Some("text".to_string()),
75 text: content.into(),
76 },
77 annotation: None,
78 })
79 }
80
81 pub fn as_text(&self) -> Option<&str> {
83 match self {
84 Content::Text(text) => Some(&text.text),
85 _ => None,
86 }
87 }
88
89 pub fn as_image(&self) -> Option<(&str, &str)> {
91 match self {
92 Content::Image(image) => Some((&image.data, &image.mime_type)),
93 _ => None,
94 }
95 }
96
97 pub fn with_audience(mut self, audience: Vec<Role>) -> Self {
99 let annotation = match &mut self {
100 Content::Text(text) => &mut text.annotation,
101 Content::Image(image) => &mut image.annotation,
102 Content::Resource(resource) => &mut resource.annotation,
103 };
104 *annotation = Some(match annotation.take() {
105 Some(mut a) => {
106 a.audience = Some(audience);
107 a
108 }
109 None => Annotation {
110 audience: Some(audience),
111 priority: None,
112 timestamp: None,
113 },
114 });
115 self
116 }
117
118 pub fn with_priority(mut self, priority: f32) -> Self {
122 if !(0.0 ..= 1.0).contains(&priority) {
123 panic!("Priority must be between 0.0 and 1.0");
124 }
125 let annotation = match &mut self {
126 Content::Text(text) => &mut text.annotation,
127 Content::Image(image) => &mut image.annotation,
128 Content::Resource(resource) => &mut resource.annotation,
129 };
130 *annotation = Some(match annotation.take() {
131 Some(mut a) => {
132 a.priority = Some(priority);
133 a
134 }
135 None => Annotation {
136 audience: None,
137 priority: Some(priority),
138 timestamp: None,
139 },
140 });
141 self
142 }
143
144 pub fn audience(&self) -> Option<&Vec<Role>> {
146 match self {
147 Content::Text(text) => text.annotation.as_ref().and_then(|a| a.audience.as_ref()),
148 Content::Image(image) => image.annotation.as_ref().and_then(|a| a.audience.as_ref()),
149 Content::Resource(resource) => resource
150 .annotation
151 .as_ref()
152 .and_then(|a| a.audience.as_ref()),
153 }
154 }
155
156 pub fn priority(&self) -> Option<f32> {
158 match self {
159 Content::Text(text) => text.annotation.as_ref().and_then(|a| a.priority),
160 Content::Image(image) => image.annotation.as_ref().and_then(|a| a.priority),
161 Content::Resource(resource) => resource.annotation.as_ref().and_then(|a| a.priority),
162 }
163 }
164
165 pub fn unannotated(&self) -> Self {
166 match self {
167 Content::Text(text) => Content::text(text.text.clone()),
168 Content::Image(image) => Content::image(image.data.clone(), image.mime_type.clone()),
169 Content::Resource(resource) => Content::resource(resource.resource.clone()),
170 }
171 }
172}