1use std::{collections::HashMap, str::FromStr};
2
3use serde::{Deserialize, Serialize};
4#[cfg(feature = "im")]
5use serde_json::json;
6use strum_macros::EnumString;
7
8use crate::card::{
9 components::{
10 content_components::{plain_text::PlainText, title::FeishuCardTitle},
11 CardElement,
12 },
13 text::CustomTextSize,
14};
15
16#[cfg(feature = "im")]
17use crate::service::im::v1::message::SendMessageTrait;
18
19pub mod components;
23
24pub mod href;
28
29pub mod icon;
33
34pub mod interactions;
38
39pub mod text;
43
44#[derive(Debug, Serialize, Deserialize, Default)]
90pub struct FeishuCard {
91 #[serde(skip_serializing_if = "Option::is_none")]
93 pub config: Option<FeishuCardConfig>,
94 pub i18n_header: HashMap<FeishuCardLanguage, FeishuCardTitle>,
96 pub i18n_elements: HashMap<FeishuCardLanguage, Vec<CardElement>>,
98}
99
100#[cfg(feature = "im")]
101impl SendMessageTrait for FeishuCard {
102 fn msg_type(&self) -> String {
103 "interactive".to_string()
104 }
105
106 fn content(&self) -> String {
107 json!(self).to_string()
108 }
109}
110
111impl FeishuCard {
112 pub fn new() -> Self {
116 let lng = FeishuCardLanguage::ZhCN;
117 let mut header = HashMap::new();
118 header.insert(lng, FeishuCardTitle::default());
119 let mut elements = HashMap::new();
120 elements.insert(lng, vec![]);
121 Self {
122 config: None,
123 i18n_header: header,
124 i18n_elements: elements,
125 }
126 }
127
128 pub fn config(mut self, config: FeishuCardConfig) -> Self {
133 self.config = Some(config);
134 self
135 }
136
137 pub fn header(
143 mut self,
144 lng: &str,
145 header: FeishuCardTitle,
146 ) -> Result<Self, crate::core::error::LarkAPIError> {
147 let language: FeishuCardLanguage = lng.parse().map_err(|e| {
148 crate::core::error::LarkAPIError::illegal_param(format!(
149 "unknown language '{lng}': {e}"
150 ))
151 })?;
152 let origin_header = self.i18n_header.entry(language).or_default();
153 *origin_header = header;
154
155 Ok(self)
156 }
157
158 pub fn elements(
164 mut self,
165 lng: &str,
166 elements: Vec<CardElement>,
167 ) -> Result<Self, crate::core::error::LarkAPIError> {
168 let language: FeishuCardLanguage = lng.parse().map_err(|e| {
169 crate::core::error::LarkAPIError::illegal_param(format!(
170 "unknown language '{lng}': {e}"
171 ))
172 })?;
173 let self_elements = self.i18n_elements.entry(language).or_default();
174 self_elements.extend(elements);
175 Ok(self)
176 }
177}
178
179#[derive(Debug, Serialize, Deserialize, Default)]
181pub struct FeishuCardConfig {
182 #[serde(skip_serializing_if = "Option::is_none")]
187 enable_forward: Option<bool>,
188 #[serde(skip_serializing_if = "Option::is_none")]
195 update_multi: Option<bool>,
196 width_mode: Option<FeishuCardWidthMode>,
201 #[serde(skip_serializing_if = "Option::is_none")]
207 use_custom_translation: Option<bool>,
208 #[serde(skip_serializing_if = "Option::is_none")]
210 enable_forward_interaction: Option<bool>,
211 #[serde(skip_serializing_if = "Option::is_none")]
213 pub style: Option<FeishuCardStyle>,
214}
215
216impl FeishuCardConfig {
217 pub fn new() -> Self {
219 Self::default()
220 }
221
222 pub fn enable_forward(mut self, enable_forward: bool) -> Self {
227 self.enable_forward = Some(enable_forward);
228 self
229 }
230
231 pub fn update_multi(mut self, update_multi: bool) -> Self {
236 self.update_multi = Some(update_multi);
237 self
238 }
239
240 pub fn width_mode(mut self, width_mode: FeishuCardWidthMode) -> Self {
245 self.width_mode = Some(width_mode);
246 self
247 }
248
249 pub fn use_custom_translation(mut self, use_custom_translation: bool) -> Self {
254 self.use_custom_translation = Some(use_custom_translation);
255 self
256 }
257
258 pub fn enable_forward_interaction(mut self, enable_forward_interaction: bool) -> Self {
263 self.enable_forward_interaction = Some(enable_forward_interaction);
264 self
265 }
266
267 pub fn style(mut self, style: FeishuCardStyle) -> Self {
272 self.style = Some(style);
273 self
274 }
275}
276
277#[derive(Debug, Serialize, Deserialize, Default)]
279#[serde(rename_all = "lowercase")]
280pub enum FeishuCardWidthMode {
281 #[default]
283 Default,
284 Fill,
286}
287
288#[derive(Debug, Serialize, Deserialize)]
292pub struct FeishuCardStyle {
293 #[serde(skip_serializing_if = "Option::is_none")]
296 text_size: Option<HashMap<String, CustomTextSize>>,
297 #[serde(skip_serializing_if = "Option::is_none")]
300 color: Option<HashMap<String, String>>,
301}
302
303#[derive(Debug, Serialize, Deserialize, Default, Eq, PartialEq, Hash, Clone, Copy)]
307pub enum FeishuCardLanguage {
308 #[serde(rename = "zh_cn")]
310 #[default]
311 ZhCN,
312 #[serde(rename = "en_us")]
314 EnUS,
315 #[serde(rename = "ja_jp")]
317 JaJP,
318 #[serde(rename = "zh_hk")]
320 ZhHK,
321 #[serde(rename = "zh_tw")]
323 ZhTW,
324}
325
326impl FromStr for FeishuCardLanguage {
327 type Err = String;
328
329 fn from_str(s: &str) -> Result<Self, Self::Err> {
330 match s.to_ascii_lowercase().as_str() {
331 "zh_cn" => Ok(FeishuCardLanguage::ZhCN),
332 "en_us" => Ok(FeishuCardLanguage::EnUS),
333 "ja_jp" => Ok(FeishuCardLanguage::JaJP),
334 "zh_hk" => Ok(FeishuCardLanguage::ZhHK),
335 "zh_tw" => Ok(FeishuCardLanguage::ZhTW),
336 _ => Err(format!("unknown language: {s}")),
337 }
338 }
339}
340
341#[derive(Debug, Serialize, Deserialize)]
344pub struct TextTag {
345 tag: String,
347 text: Option<PlainText>,
349 color: Option<String>,
351}
352
353impl Default for TextTag {
354 fn default() -> Self {
355 TextTag {
356 tag: "text_tag".to_string(),
357 text: None,
358 color: None,
359 }
360 }
361}
362
363impl TextTag {
364 pub fn new() -> Self {
366 Self::default()
367 }
368
369 pub fn text(mut self, text: PlainText) -> Self {
374 self.text = Some(text);
375 self
376 }
377
378 pub fn color(mut self, color: &str) -> Self {
383 self.color = Some(color.to_string());
384 self
385 }
386}
387
388#[derive(Debug, Serialize, Deserialize, Default, EnumString)]
392#[serde(rename_all = "lowercase")]
393#[strum(serialize_all = "lowercase")]
394pub enum FeishuCardHeaderTemplate {
395 Blue,
397 Wathet,
399 Turquoise,
401 Green,
403 Yellow,
405 Orange,
407 Red,
409 Carmine,
411 Violet,
413 Purple,
415 Indigo,
417 Grey,
419 #[default]
421 Default,
422}
423
424#[derive(Debug, Serialize, Deserialize, Default)]
428#[serde(rename_all = "lowercase")]
429pub enum MessageCardColor {
430 Neutral,
432 #[default]
434 Blue,
435 Turquoise,
437 Lime,
439 Orange,
441 Violet,
443 Indigo,
445 Wathet,
447 Green,
449 Yellow,
451 Red,
453 Purple,
455 Carmine,
457}