1use std::sync::Arc;
2use serde::{Serialize, Serializer};
3
4use crate::runtime::handle::ExtensionHandle;
5use crate::runtime::handlers::SingleEntryContext;
6use crate::runtime::state::{MessageTabContentHandler, MessageTabHandlers, MessageTabVisibilityHandler};
7use crate::error::Error;
8
9#[derive(Serialize, Clone, Copy, Debug, PartialEq, Eq)]
11#[serde(rename_all = "lowercase")]
12pub enum Language {
13 Text,
14 Json,
15 Xml,
16 Html,
17 Css,
18 JavaScript,
19 Yaml,
20}
21
22#[derive(Serialize, Clone, Debug)]
24#[serde(rename_all = "camelCase")]
25pub struct TabContent {
26 text: String,
27 language: Language,
28}
29
30impl TabContent {
31 pub fn new(text: impl Into<String>, language: Language) -> Self {
33 Self {
34 text: text.into(),
35 language,
36 }
37 }
38
39 pub fn plain(text: impl Into<String>) -> Self {
41 Self::new(text, Language::Text)
42 }
43
44 pub fn json(text: impl Into<String>) -> Self {
46 Self::new(text, Language::Json)
47 }
48
49 pub fn xml(text: impl Into<String>) -> Self {
51 Self::new(text, Language::Xml)
52 }
53
54 pub fn html(text: impl Into<String>) -> Self {
56 Self::new(text, Language::Html)
57 }
58
59 pub fn css(text: impl Into<String>) -> Self {
61 Self::new(text, Language::Css)
62 }
63
64 pub fn javascript(text: impl Into<String>) -> Self {
66 Self::new(text, Language::JavaScript)
67 }
68
69 pub fn yaml(text: impl Into<String>) -> Self {
71 Self::new(text, Language::Yaml)
72 }
73}
74
75#[derive(Serialize)]
116#[serde(rename_all = "camelCase")]
117pub struct MessageTab {
118 id: String,
119 label: String,
120 #[serde(skip)]
121 content: MessageTabContentHandler,
122 #[serde(rename = "isConditional", serialize_with = "serialize_is_some")]
123 visibility: Option<MessageTabVisibilityHandler>,
124}
125
126impl MessageTab {
127 pub fn new<F, Fut>(id: impl Into<String>, label: impl Into<String>, content: F) -> Self
129 where
130 F: Fn(SingleEntryContext, ExtensionHandle) -> Fut + Send + Sync + 'static,
131 Fut: Future<Output = Result<TabContent, Error>> + Send + 'static,
132 {
133 Self {
134 id: id.into(),
135 label: label.into(),
136 content: Arc::new(move |ctx, handle| {
137 let fut = content(ctx, handle);
138 Box::pin(async move { fut.await.map_err(Error::into_jrpc) })
139 }),
140 visibility: None,
141 }
142 }
143
144 pub fn visible_when<F, Fut>(mut self, visibility: F) -> Self
149 where
150 F: Fn(SingleEntryContext, ExtensionHandle) -> Fut + Send + Sync + 'static,
151 Fut: Future<Output = Result<bool, Error>> + Send + 'static,
152 {
153 self.visibility = Some(Arc::new(move |ctx, handle| {
154 let fut = visibility(ctx, handle);
155 Box::pin(async move { fut.await.map_err(Error::into_jrpc) })
156 }));
157 self
158 }
159
160 pub(crate) fn extract_handlers(&self) -> (String, MessageTabHandlers) {
161 (
162 self.id.clone(),
163 MessageTabHandlers {
164 content: Arc::clone(&self.content),
165 visibility: self.visibility.as_ref().map(Arc::clone),
166 },
167 )
168 }
169}
170
171fn serialize_is_some<S: Serializer>(
172 visibility: &Option<MessageTabVisibilityHandler>,
173 serializer: S,
174) -> Result<S::Ok, S::Error> {
175 serializer.serialize_bool(visibility.is_some())
176}