open_lark/card/components/interactive_components/
button.rs1use serde::{Deserialize, Serialize};
2
3use crate::card::{
4 components::{
5 content_components::plain_text::PlainText, interactive_components::input::InputConfirm,
6 },
7 icon::FeishuCardTextIcon,
8 interactions::Behaviors,
9};
10
11#[derive(Debug, Serialize, Deserialize)]
12pub struct FeishuCardButton {
13 tag: String,
15 #[serde(skip_serializing_if = "Option::is_none")]
27 r#type: Option<String>,
28 #[serde(skip_serializing_if = "Option::is_none")]
35 size: Option<String>,
36 #[serde(skip_serializing_if = "Option::is_none")]
42 width: Option<String>,
43 #[serde(skip_serializing_if = "Option::is_none")]
45 text: Option<PlainText>,
46 #[serde(skip_serializing_if = "Option::is_none")]
48 icon: Option<FeishuCardTextIcon>,
49 #[serde(skip_serializing_if = "Option::is_none")]
51 hover_tips: Option<PlainText>,
52 #[serde(skip_serializing_if = "Option::is_none")]
57 disabled: Option<bool>,
58 #[serde(skip_serializing_if = "Option::is_none")]
60 disabled_tips: Option<PlainText>,
61 #[serde(skip_serializing_if = "Option::is_none")]
66 confirm: Option<InputConfirm>,
67 #[serde(skip_serializing_if = "Option::is_none")]
69 behaviors: Option<Vec<Behaviors>>,
70 #[serde(skip_serializing_if = "Option::is_none")]
74 name: Option<String>,
75 #[serde(skip_serializing_if = "Option::is_none")]
82 required: Option<bool>,
83 #[serde(skip_serializing_if = "Option::is_none")]
93 action_type: Option<String>,
94}
95
96impl Default for FeishuCardButton {
97 fn default() -> Self {
98 Self {
99 tag: "button".to_string(),
100 r#type: None,
101 size: None,
102 width: None,
103 text: None,
104 icon: None,
105 hover_tips: None,
106 disabled: None,
107 disabled_tips: None,
108 confirm: None,
109 behaviors: None,
110 name: None,
111 required: None,
112 action_type: None,
113 }
114 }
115}
116
117impl FeishuCardButton {
118 pub fn new() -> Self {
119 Self::default()
120 }
121
122 pub fn r#type(mut self, typ: &str) -> Self {
124 self.r#type = Some(typ.to_string());
125 self
126 }
127
128 pub fn size(mut self, size: &str) -> Self {
129 self.size = Some(size.to_string());
130 self
131 }
132
133 pub fn width(mut self, width: &str) -> Self {
134 self.width = Some(width.to_string());
135 self
136 }
137
138 pub fn text(mut self, text: PlainText) -> Self {
139 self.text = Some(text);
140 self
141 }
142
143 pub fn icon(mut self, icon: FeishuCardTextIcon) -> Self {
144 self.icon = Some(icon);
145 self
146 }
147
148 pub fn hover_tips(mut self, hover_tips: PlainText) -> Self {
149 self.hover_tips = Some(hover_tips);
150 self
151 }
152
153 pub fn disabled(mut self, disabled: bool) -> Self {
154 self.disabled = Some(disabled);
155 self
156 }
157
158 pub fn disabled_tips(mut self, disabled_tips: PlainText) -> Self {
159 self.disabled_tips = Some(disabled_tips);
160 self
161 }
162
163 pub fn confirm(mut self, confirm: InputConfirm) -> Self {
164 self.confirm = Some(confirm);
165 self
166 }
167
168 pub fn behaviors(mut self, behaviors: Vec<Behaviors>) -> Self {
169 self.behaviors = Some(behaviors);
170 self
171 }
172
173 pub fn name(mut self, name: &str) -> Self {
174 self.name = Some(name.to_string());
175 self
176 }
177
178 pub fn required(mut self, required: bool) -> Self {
179 self.required = Some(required);
180 self
181 }
182
183 pub fn action_type(mut self, action_type: &str) -> Self {
184 self.action_type = Some(action_type.to_string());
185 self
186 }
187}
188
189#[cfg(test)]
190mod test {
191 use serde_json::json;
192
193 use crate::card::{
194 components::{
195 content_components::plain_text::PlainText,
196 interactive_components::{button::FeishuCardButton, input::InputConfirm},
197 },
198 icon::FeishuCardTextIcon,
199 interactions::{Behaviors, CallbackBehavior, FormBehavior, OpenUrlBehavior},
200 };
201
202 #[test]
203 fn test_button() {
204 let button = FeishuCardButton::new()
205 .r#type("primary")
206 .size("small")
207 .width("default")
208 .text(PlainText::text("确定"))
209 .icon(
210 FeishuCardTextIcon::new()
211 .token("chat-forbidden_outlined")
212 .color("orange")
213 .img_key("img_v2_38811724"),
214 )
215 .disabled(false)
216 .confirm(InputConfirm::new("title", "content"))
217 .behaviors(vec![
218 Behaviors::OpenUrl(
219 OpenUrlBehavior::new("https://www.baidu.com")
220 .android_url("https://developer.android.com/")
221 .ios_url("lark://msgcard/unsupported_action")
222 .pc_url("https://www.windows.com"),
223 ),
224 Behaviors::Callback(CallbackBehavior::new(json!({"key": "value"}))),
225 Behaviors::Form(FormBehavior::new().behavior("submit")),
226 ]);
227
228 let json = json!({
229 "tag": "button", "type": "primary", "size": "small", "width": "default", "text": {
234 "tag": "plain_text",
236 "content": "确定"
237 },
238 "icon": {
239 "tag": "standard_icon", "token": "chat-forbidden_outlined", "color": "orange", "img_key": "img_v2_38811724" },
245
246 "disabled": false, "confirm": {
249 "title": {
251 "tag": "plain_text",
252 "content": "title"
253 },
254 "text": {
255 "tag": "plain_text",
256 "content": "content"
257 }
258 },
259 "behaviors": [
260 {
261 "type": "open_url", "default_url": "https://www.baidu.com", "android_url": "https://developer.android.com/", "ios_url": "lark://msgcard/unsupported_action", "pc_url": "https://www.windows.com" },
267 {
268 "type": "callback", "value": {
270 "key": "value"
272 }
273 },
274 {
275 "type": "form_action", "behavior": "submit" }
278 ],
279 });
280
281 assert_eq!(serde_json::to_value(&button).unwrap(), json);
282 }
283}