open_lark/service/cloud_docs/sheets/v2/data_operation/
split_cells.rs

1use serde::{Deserialize, Serialize};
2
3use crate::{
4    core::{
5        api_req::ApiRequest,
6        api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
7        constants::AccessTokenType,
8        endpoints::cloud_docs::*,
9        req_option, SDKResult,
10    },
11    service::cloud_docs::sheets::v2::SpreadsheetService,
12};
13
14/// 拆分单元格请求
15#[derive(Serialize, Debug, Default)]
16pub struct SplitCellsRequest {
17    #[serde(skip)]
18    api_request: ApiRequest,
19    #[serde(skip)]
20    spreadsheet_token: String,
21    /// 查询范围,包含 sheetId 与单元格范围两部分,目前支持四种索引方式,详见 在线表格开发指南
22    range: String,
23}
24
25impl SplitCellsRequest {
26    pub fn builder() -> SplitCellsRequestBuilder {
27        SplitCellsRequestBuilder::default()
28    }
29}
30
31#[derive(Default)]
32pub struct SplitCellsRequestBuilder {
33    request: SplitCellsRequest,
34}
35
36impl SplitCellsRequestBuilder {
37    pub fn spreadsheet_token(mut self, spreadsheet_token: impl ToString) -> Self {
38        self.request.spreadsheet_token = spreadsheet_token.to_string();
39        self
40    }
41
42    /// 查询范围,包含 sheetId 与单元格范围两部分,目前支持四种索引方式,详见 在线表格开发指南
43    pub fn range(mut self, range: impl ToString) -> Self {
44        self.request.range = range.to_string();
45        self
46    }
47
48    pub fn build(mut self) -> SplitCellsRequest {
49        self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
50        self.request
51    }
52}
53
54#[derive(Deserialize, Debug)]
55pub struct SplitCellsResponse {
56    /// spreadsheet 的 token
57    #[serde(rename = "spreadsheetToken")]
58    pub spread_sheet_token: String,
59}
60
61impl ApiResponseTrait for SplitCellsResponse {
62    fn data_format() -> ResponseFormat {
63        ResponseFormat::Data
64    }
65}
66
67impl SpreadsheetService {
68    /// 拆分单元格
69    pub async fn split_cells(
70        &self,
71        request: SplitCellsRequest,
72        option: Option<req_option::RequestOption>,
73    ) -> SDKResult<BaseResponse<SplitCellsResponse>> {
74        let mut api_req = request.api_request;
75        api_req.api_path =
76            SHEETS_V2_SPREADSHEET_UNMERGE_CELLS.replace("{}", &request.spreadsheet_token);
77        api_req.http_method = reqwest::Method::POST;
78        api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::App];
79
80        let api_resp = crate::core::http::Transport::request(api_req, &self.config, option).await?;
81
82        Ok(api_resp)
83    }
84}
85
86#[cfg(test)]
87mod tests {
88    use crate::{
89        core::{config::Config, constants::AppType},
90        service::cloud_docs::sheets::v2::{
91            data_operation::{SplitCellsRequest, SplitCellsResponse},
92            SpreadsheetService,
93        },
94    };
95
96    fn create_service() -> SpreadsheetService {
97        let config = Config::builder()
98            .app_id("test_app_id")
99            .app_secret("test_app_secret")
100            .app_type(AppType::SelfBuild)
101            .build();
102        SpreadsheetService { config }
103    }
104
105    #[test]
106    fn test_split_cells_builder_default() {
107        let request = SplitCellsRequest::builder().build();
108
109        assert_eq!(request.spreadsheet_token, "");
110        assert_eq!(request.range, "");
111    }
112
113    #[test]
114    fn test_split_cells_builder_basic() {
115        let request = SplitCellsRequest::builder()
116            .spreadsheet_token("test_token")
117            .range("Sheet1!A1:B2")
118            .build();
119
120        assert_eq!(request.spreadsheet_token, "test_token");
121        assert_eq!(request.range, "Sheet1!A1:B2");
122    }
123
124    #[test]
125    fn test_split_cells_builder_all_options() {
126        let request = SplitCellsRequest::builder()
127            .spreadsheet_token("spreadsheet_abc123")
128            .range("Data!C3:F6")
129            .build();
130
131        assert_eq!(request.spreadsheet_token, "spreadsheet_abc123");
132        assert_eq!(request.range, "Data!C3:F6");
133    }
134
135    #[test]
136    fn test_split_cells_builder_chaining() {
137        let request = SplitCellsRequest::builder()
138            .spreadsheet_token("chain_test")
139            .range("Summary!A1:D1")
140            .build();
141
142        assert_eq!(request.spreadsheet_token, "chain_test");
143        assert_eq!(request.range, "Summary!A1:D1");
144    }
145
146    #[test]
147    fn test_split_cells_single_merged_cell() {
148        let request = SplitCellsRequest::builder()
149            .spreadsheet_token("single_cell_test")
150            .range("Sheet1!A1:A1")
151            .build();
152
153        assert_eq!(request.spreadsheet_token, "single_cell_test");
154        assert_eq!(request.range, "Sheet1!A1:A1");
155    }
156
157    #[test]
158    fn test_split_cells_large_merged_range() {
159        let request = SplitCellsRequest::builder()
160            .spreadsheet_token("large_range_test")
161            .range("Data!A1:Z100")
162            .build();
163
164        assert_eq!(request.spreadsheet_token, "large_range_test");
165        assert_eq!(request.range, "Data!A1:Z100");
166    }
167
168    #[test]
169    fn test_split_cells_row_range() {
170        let request = SplitCellsRequest::builder()
171            .spreadsheet_token("row_range_test")
172            .range("Sheet1!A1:Z1")
173            .build();
174
175        assert_eq!(request.spreadsheet_token, "row_range_test");
176        assert_eq!(request.range, "Sheet1!A1:Z1");
177    }
178
179    #[test]
180    fn test_split_cells_column_range() {
181        let request = SplitCellsRequest::builder()
182            .spreadsheet_token("column_range_test")
183            .range("Sheet1!A1:A50")
184            .build();
185
186        assert_eq!(request.spreadsheet_token, "column_range_test");
187        assert_eq!(request.range, "Sheet1!A1:A50");
188    }
189
190    #[test]
191    fn test_split_cells_with_unicode_ranges() {
192        let request = SplitCellsRequest::builder()
193            .spreadsheet_token("unicode_test")
194            .range("数据表!A1:D4")
195            .build();
196
197        assert_eq!(request.spreadsheet_token, "unicode_test");
198        assert_eq!(request.range, "数据表!A1:D4");
199    }
200
201    #[test]
202    fn test_split_cells_with_special_characters() {
203        let request = SplitCellsRequest::builder()
204            .spreadsheet_token("special_chars_test")
205            .range("'Sheet With Spaces'!A1:B5")
206            .build();
207
208        assert_eq!(request.spreadsheet_token, "special_chars_test");
209        assert_eq!(request.range, "'Sheet With Spaces'!A1:B5");
210    }
211
212    #[test]
213    fn test_split_cells_different_sheets() {
214        let sheets_and_ranges = [
215            ("Sheet1", "A1:B2"),
216            ("Summary", "C1:F1"),
217            ("Data", "A5:D10"),
218            ("第一页", "B3:E8"),
219        ];
220
221        for (sheet, range) in sheets_and_ranges.iter() {
222            let full_range = format!("{}!{}", sheet, range);
223            let request = SplitCellsRequest::builder()
224                .spreadsheet_token("multi_sheet_test")
225                .range(&full_range)
226                .build();
227
228            assert_eq!(request.range, full_range);
229        }
230    }
231
232    #[test]
233    fn test_split_cells_serialization() {
234        let request = SplitCellsRequest::builder()
235            .spreadsheet_token("serialization_test")
236            .range("Sheet1!A1:C3")
237            .build();
238
239        let serialized = serde_json::to_string(&request);
240        assert!(serialized.is_ok());
241
242        let json_value: serde_json::Value = serde_json::from_str(&serialized.unwrap()).unwrap();
243        assert_eq!(json_value["range"], "Sheet1!A1:C3");
244    }
245
246    #[test]
247    fn test_split_cells_response_deserialization() {
248        let response_json = serde_json::json!({
249            "spreadsheetToken": "test_token_123"
250        });
251
252        let response: SplitCellsResponse = serde_json::from_value(response_json).unwrap();
253
254        assert_eq!(response.spread_sheet_token, "test_token_123");
255    }
256
257    #[test]
258    fn test_split_cells_complex_range_references() {
259        let complex_ranges = vec![
260            "Sheet1!A1:D5",
261            "'Data Sheet'!B2:F10",
262            "工作表!C3:G7",
263            "Sheet-Name_123!A10:E15",
264            "'Sheet (1)'!D1:H4",
265        ];
266
267        for range in complex_ranges {
268            let request = SplitCellsRequest::builder()
269                .spreadsheet_token("complex_ref_test")
270                .range(range)
271                .build();
272
273            assert_eq!(request.range, range);
274        }
275    }
276
277    #[test]
278    fn test_split_cells_service_creation() {
279        let service = create_service();
280        assert_eq!(service.config.app_id, "test_app_id");
281        assert_eq!(service.config.app_secret, "test_app_secret");
282        assert!(matches!(service.config.app_type, AppType::SelfBuild));
283    }
284
285    #[test]
286    fn test_split_cells_builder_overwrites() {
287        let request = SplitCellsRequest::builder()
288            .spreadsheet_token("original_token")
289            .spreadsheet_token("final_token") // Should overwrite
290            .range("Sheet1!A1:B2")
291            .range("Sheet2!C3:D4") // Should overwrite
292            .build();
293
294        assert_eq!(request.spreadsheet_token, "final_token");
295        assert_eq!(request.range, "Sheet2!C3:D4");
296    }
297
298    #[test]
299    fn test_split_cells_very_long_token() {
300        let very_long_token = "a".repeat(1000);
301        let request = SplitCellsRequest::builder()
302            .spreadsheet_token(&very_long_token)
303            .range("Sheet1!A1:B2")
304            .build();
305
306        assert_eq!(request.spreadsheet_token, very_long_token);
307        assert_eq!(request.range, "Sheet1!A1:B2");
308    }
309
310    #[test]
311    fn test_split_cells_response_struct_debug() {
312        let response = SplitCellsResponse {
313            spread_sheet_token: "debug_test".to_string(),
314        };
315
316        let debug_str = format!("{:?}", response);
317        assert!(debug_str.contains("debug_test"));
318        assert!(debug_str.contains("SplitCellsResponse"));
319    }
320
321    #[test]
322    fn test_split_cells_request_struct_debug() {
323        let request = SplitCellsRequest::builder()
324            .spreadsheet_token("debug_token")
325            .range("Sheet1!A1:B2")
326            .build();
327
328        let debug_str = format!("{:?}", request);
329        assert!(debug_str.contains("debug_token"));
330        assert!(debug_str.contains("Sheet1!A1:B2"));
331    }
332
333    #[test]
334    fn test_split_cells_empty_strings() {
335        let request = SplitCellsRequest::builder()
336            .spreadsheet_token("")
337            .range("")
338            .build();
339
340        assert_eq!(request.spreadsheet_token, "");
341        assert_eq!(request.range, "");
342    }
343
344    #[test]
345    fn test_split_cells_multiple_merged_areas() {
346        let merged_areas = vec![
347            "Sheet1!A1:B2",  // 2x2 merged area
348            "Sheet1!D1:G1",  // Row merge
349            "Sheet1!A5:A10", // Column merge
350            "Sheet1!C3:E5",  // 3x3 merged area
351        ];
352
353        for range in merged_areas {
354            let request = SplitCellsRequest::builder()
355                .spreadsheet_token("multiple_areas_test")
356                .range(range)
357                .build();
358
359            assert_eq!(request.range, range);
360        }
361    }
362
363    #[test]
364    fn test_split_cells_large_spreadsheet_ranges() {
365        let large_ranges = vec![
366            "Sheet1!A1:ZZ1000", // Very large range
367            "Data!A1:IV65536",  // Excel-style large range
368            "Report!AA1:ZZ100", // Wide range with double-letter columns
369        ];
370
371        for range in large_ranges {
372            let request = SplitCellsRequest::builder()
373                .spreadsheet_token("large_spreadsheet_test")
374                .range(range)
375                .build();
376
377            assert_eq!(request.range, range);
378        }
379    }
380
381    #[test]
382    fn test_split_cells_various_sheet_names() {
383        let sheet_names_with_ranges = vec![
384            ("普通工作表", "A1:B2"),
385            ("Sheet_123", "C1:D5"),
386            ("'Special-Name (1)'", "E1:F3"),
387            ("数据分析报表", "A10:C15"),
388            ("Sheet With Spaces", "G1:H2"),
389        ];
390
391        for (sheet_name, range) in sheet_names_with_ranges {
392            let full_range = if sheet_name.contains(' ') || sheet_name.contains('(') {
393                format!("'{}'!{}", sheet_name, range)
394            } else {
395                format!("{}!{}", sheet_name, range)
396            };
397
398            let request = SplitCellsRequest::builder()
399                .spreadsheet_token("various_sheets_test")
400                .range(&full_range)
401                .build();
402
403            assert_eq!(request.range, full_range);
404        }
405    }
406
407    #[test]
408    fn test_split_cells_edge_case_ranges() {
409        let edge_cases = vec![
410            "Sheet1!A1:A1",     // Single cell
411            "Sheet1!A1:B1",     // Two cells horizontal
412            "Sheet1!A1:A2",     // Two cells vertical
413            "Sheet1!Z99:AA100", // Cross column boundary
414        ];
415
416        for range in edge_cases {
417            let request = SplitCellsRequest::builder()
418                .spreadsheet_token("edge_cases_test")
419                .range(range)
420                .build();
421
422            assert_eq!(request.range, range);
423        }
424    }
425}