open_lark/service/cloud_docs/sheets/v3/condition_format/
create.rs

1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5    core::{
6        api_req::ApiRequest,
7        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8        constants::AccessTokenType,
9        endpoints::cloud_docs::*,
10        http::Transport,
11        req_option::RequestOption,
12        SDKResult,
13    },
14    impl_executable_builder_owned,
15    service::sheets::v3::SpreadsheetSheetService,
16};
17
18impl SpreadsheetSheetService {
19    /// 批量创建条件格式
20    pub async fn create_condition_formats(
21        &self,
22        request: CreateConditionFormatsRequest,
23        option: Option<RequestOption>,
24    ) -> SDKResult<BaseResponse<CreateConditionFormatsResponseData>> {
25        let mut api_req = request.api_request;
26        api_req.http_method = Method::POST;
27        api_req.api_path = SHEETS_V3_SPREADSHEET_CONDITION_FORMAT
28            .replace("{}", &request.spreadsheet_token)
29            .replace("{}", &request.sheet_id);
30        api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
31
32        let api_resp = Transport::request(api_req, &self.config, option).await?;
33
34        Ok(api_resp)
35    }
36}
37
38/// 批量创建条件格式请求
39#[derive(Default, Debug, Serialize, Deserialize)]
40pub struct CreateConditionFormatsRequest {
41    #[serde(skip)]
42    api_request: ApiRequest,
43    /// spreadsheet 的 token
44    spreadsheet_token: String,
45    /// sheet 的 id
46    sheet_id: String,
47    /// 条件格式规则列表
48    condition_formats: Vec<ConditionFormatRule>,
49}
50
51impl CreateConditionFormatsRequest {
52    pub fn builder() -> CreateConditionFormatsRequestBuilder {
53        CreateConditionFormatsRequestBuilder::default()
54    }
55}
56
57#[derive(Default)]
58pub struct CreateConditionFormatsRequestBuilder {
59    request: CreateConditionFormatsRequest,
60}
61
62impl CreateConditionFormatsRequestBuilder {
63    pub fn spreadsheet_token(mut self, spreadsheet_token: impl ToString) -> Self {
64        self.request.spreadsheet_token = spreadsheet_token.to_string();
65        self
66    }
67
68    pub fn sheet_id(mut self, sheet_id: impl ToString) -> Self {
69        self.request.sheet_id = sheet_id.to_string();
70        self
71    }
72
73    pub fn condition_formats(mut self, condition_formats: Vec<ConditionFormatRule>) -> Self {
74        self.request.condition_formats = condition_formats;
75        self
76    }
77
78    pub fn add_condition_format(mut self, condition_format: ConditionFormatRule) -> Self {
79        self.request.condition_formats.push(condition_format);
80        self
81    }
82
83    pub fn build(mut self) -> CreateConditionFormatsRequest {
84        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
85        self.request
86    }
87}
88
89/// 条件格式规则
90#[derive(Default, Debug, Serialize, Deserialize)]
91pub struct ConditionFormatRule {
92    /// 应用范围
93    pub range: String,
94    /// 条件类型
95    pub condition_type: String,
96    /// 条件值
97    #[serde(skip_serializing_if = "Option::is_none")]
98    pub condition_values: Option<Vec<String>>,
99    /// 格式设置
100    pub format: FormatStyle,
101    /// 条件格式 ID(仅在响应时存在)
102    #[serde(skip_serializing_if = "Option::is_none")]
103    pub cf_id: Option<String>,
104}
105
106/// 格式样式
107#[derive(Default, Debug, Serialize, Deserialize)]
108pub struct FormatStyle {
109    /// 背景颜色
110    #[serde(skip_serializing_if = "Option::is_none")]
111    pub background_color: Option<String>,
112    /// 文字颜色
113    #[serde(skip_serializing_if = "Option::is_none")]
114    pub text_color: Option<String>,
115    /// 是否加粗
116    #[serde(skip_serializing_if = "Option::is_none")]
117    pub bold: Option<bool>,
118    /// 是否斜体
119    #[serde(skip_serializing_if = "Option::is_none")]
120    pub italic: Option<bool>,
121    /// 是否下划线
122    #[serde(skip_serializing_if = "Option::is_none")]
123    pub underline: Option<bool>,
124    /// 是否删除线
125    #[serde(skip_serializing_if = "Option::is_none")]
126    pub strikethrough: Option<bool>,
127}
128
129impl ConditionFormatRule {
130    /// 创建数值比较条件格式
131    pub fn number_comparison(
132        range: impl ToString,
133        comparison_type: impl ToString,
134        value: f64,
135        format: FormatStyle,
136    ) -> Self {
137        Self {
138            range: range.to_string(),
139            condition_type: comparison_type.to_string(),
140            condition_values: Some(vec![value.to_string()]),
141            format,
142            cf_id: None,
143        }
144    }
145
146    /// 创建大于条件格式
147    pub fn greater_than(range: impl ToString, value: f64, format: FormatStyle) -> Self {
148        Self::number_comparison(range, "NUMBER_GREATER", value, format)
149    }
150
151    /// 创建小于条件格式
152    pub fn less_than(range: impl ToString, value: f64, format: FormatStyle) -> Self {
153        Self::number_comparison(range, "NUMBER_LESS", value, format)
154    }
155
156    /// 创建等于条件格式
157    pub fn equal_to(range: impl ToString, value: f64, format: FormatStyle) -> Self {
158        Self::number_comparison(range, "NUMBER_EQ", value, format)
159    }
160
161    /// 创建文本包含条件格式
162    pub fn text_contains(range: impl ToString, text: impl ToString, format: FormatStyle) -> Self {
163        Self {
164            range: range.to_string(),
165            condition_type: "TEXT_CONTAINS".to_string(),
166            condition_values: Some(vec![text.to_string()]),
167            format,
168            cf_id: None,
169        }
170    }
171
172    /// 创建重复值条件格式
173    pub fn duplicate_values(range: impl ToString, format: FormatStyle) -> Self {
174        Self {
175            range: range.to_string(),
176            condition_type: "DUPLICATE".to_string(),
177            condition_values: None,
178            format,
179            cf_id: None,
180        }
181    }
182
183    /// 创建空值条件格式
184    pub fn blank_values(range: impl ToString, format: FormatStyle) -> Self {
185        Self {
186            range: range.to_string(),
187            condition_type: "BLANK".to_string(),
188            condition_values: None,
189            format,
190            cf_id: None,
191        }
192    }
193}
194
195impl FormatStyle {
196    /// 创建背景颜色格式
197    pub fn background_color(color: impl ToString) -> Self {
198        Self {
199            background_color: Some(color.to_string()),
200            text_color: None,
201            bold: None,
202            italic: None,
203            underline: None,
204            strikethrough: None,
205        }
206    }
207
208    /// 创建文字颜色格式
209    pub fn text_color(color: impl ToString) -> Self {
210        Self {
211            background_color: None,
212            text_color: Some(color.to_string()),
213            bold: None,
214            italic: None,
215            underline: None,
216            strikethrough: None,
217        }
218    }
219
220    /// 创建字体样式格式
221    pub fn font_style(bold: bool, italic: bool, underline: bool) -> Self {
222        Self {
223            background_color: None,
224            text_color: None,
225            bold: Some(bold),
226            italic: Some(italic),
227            underline: Some(underline),
228            strikethrough: None,
229        }
230    }
231
232    /// 设置背景颜色
233    pub fn with_background_color(mut self, color: impl ToString) -> Self {
234        self.background_color = Some(color.to_string());
235        self
236    }
237
238    /// 设置文字颜色
239    pub fn with_text_color(mut self, color: impl ToString) -> Self {
240        self.text_color = Some(color.to_string());
241        self
242    }
243
244    /// 设置加粗
245    pub fn with_bold(mut self, bold: bool) -> Self {
246        self.bold = Some(bold);
247        self
248    }
249
250    /// 设置斜体
251    pub fn with_italic(mut self, italic: bool) -> Self {
252        self.italic = Some(italic);
253        self
254    }
255
256    /// 设置下划线
257    pub fn with_underline(mut self, underline: bool) -> Self {
258        self.underline = Some(underline);
259        self
260    }
261
262    /// 设置删除线
263    pub fn with_strikethrough(mut self, strikethrough: bool) -> Self {
264        self.strikethrough = Some(strikethrough);
265        self
266    }
267}
268
269/// 条件格式信息
270#[derive(Deserialize, Debug)]
271pub struct ConditionFormatInfo {
272    /// 条件格式 ID
273    pub cf_id: String,
274    /// 条件格式规则详细信息
275    #[serde(flatten)]
276    pub condition_format: ConditionFormatRule,
277}
278
279/// 批量创建条件格式响应体最外层
280#[derive(Deserialize, Debug)]
281pub struct CreateConditionFormatsResponseData {
282    /// 创建的条件格式列表
283    pub items: Vec<ConditionFormatInfo>,
284    /// 创建成功的数量
285    #[serde(default)]
286    pub created_count: u32,
287}
288
289impl ApiResponseTrait for CreateConditionFormatsResponseData {
290    fn data_format() -> ResponseFormat {
291        ResponseFormat::Data
292    }
293}
294
295impl_executable_builder_owned!(
296    CreateConditionFormatsRequestBuilder,
297    SpreadsheetSheetService,
298    CreateConditionFormatsRequest,
299    BaseResponse<CreateConditionFormatsResponseData>,
300    create_condition_formats
301);
302
303#[cfg(test)]
304#[allow(unused_variables, unused_unsafe)]
305mod test {
306    use super::*;
307    use serde_json::json;
308
309    #[test]
310    fn test_condition_format_rule_creation() {
311        let format = FormatStyle::background_color("#FF0000").with_text_color("#FFFFFF");
312        let rule = ConditionFormatRule::greater_than("A1:A10", 100.0, format);
313
314        assert_eq!(rule.range, "A1:A10");
315        assert_eq!(rule.condition_type, "NUMBER_GREATER");
316        assert_eq!(rule.condition_values.as_ref().unwrap()[0], "100");
317        assert_eq!(rule.format.background_color.as_ref().unwrap(), "#FF0000");
318        assert_eq!(rule.format.text_color.as_ref().unwrap(), "#FFFFFF");
319    }
320
321    #[test]
322    fn test_create_condition_formats_response() {
323        let json = json!({
324            "items": [
325                {
326                    "cf_id": "cf_001",
327                    "range": "A1:A10",
328                    "condition_type": "NUMBER_GREATER",
329                    "condition_values": ["100"],
330                    "format": {
331                        "background_color": "#FF0000",
332                        "text_color": "#FFFFFF",
333                        "bold": true
334                    }
335                }
336            ],
337            "created_count": 1
338        });
339
340        let response: CreateConditionFormatsResponseData = serde_json::from_value(json).unwrap();
341        assert_eq!(response.items.len(), 1);
342        assert_eq!(response.items[0].cf_id, "cf_001");
343        assert_eq!(response.created_count, 1);
344    }
345}