open_lark/service/cloud_docs/comments/
create.rs1use reqwest::Method;
2use serde::{Deserialize, Serialize};
3
4use crate::{
5 core::{
6 api_req::ApiRequest,
7 api_resp::{ApiResponseTrait, BaseResponse, ResponseFormat},
8 config::Config,
9 constants::AccessTokenType,
10 endpoints::cloud_docs::*,
11 http::Transport,
12 req_option::RequestOption,
13 SDKResult,
14 },
15 impl_executable_builder_owned,
16};
17
18use super::list::{ContentElement, ReplyContent, TextRun};
19
20#[derive(Debug, Serialize, Default, Clone)]
22pub struct CreateCommentRequest {
23 #[serde(skip)]
24 api_request: ApiRequest,
25 #[serde(skip)]
27 file_token: String,
28 #[serde(skip)]
30 file_type: String,
31 content: ReplyContent,
33 #[serde(skip_serializing_if = "Option::is_none")]
35 user_id_type: Option<String>,
36}
37
38impl CreateCommentRequest {
39 pub fn builder() -> CreateCommentRequestBuilder {
40 CreateCommentRequestBuilder::default()
41 }
42
43 pub fn new(file_token: impl ToString, file_type: impl ToString, content: ReplyContent) -> Self {
44 Self {
45 file_token: file_token.to_string(),
46 file_type: file_type.to_string(),
47 content,
48 ..Default::default()
49 }
50 }
51
52 pub fn with_text(
54 file_token: impl ToString,
55 file_type: impl ToString,
56 text: impl ToString,
57 ) -> Self {
58 let content = ReplyContent {
59 elements: vec![ContentElement {
60 element_type: "text_run".to_string(),
61 text_run: Some(TextRun {
62 text: text.to_string(),
63 style: None,
64 }),
65 }],
66 };
67
68 Self::new(file_token, file_type, content)
69 }
70}
71
72#[derive(Default)]
73pub struct CreateCommentRequestBuilder {
74 request: CreateCommentRequest,
75}
76
77impl CreateCommentRequestBuilder {
78 pub fn file_token(mut self, file_token: impl ToString) -> Self {
80 self.request.file_token = file_token.to_string();
81 self
82 }
83
84 pub fn file_type(mut self, file_type: impl ToString) -> Self {
86 self.request.file_type = file_type.to_string();
87 self
88 }
89
90 pub fn with_doc_type(mut self) -> Self {
92 self.request.file_type = "doc".to_string();
93 self
94 }
95
96 pub fn with_docx_type(mut self) -> Self {
98 self.request.file_type = "docx".to_string();
99 self
100 }
101
102 pub fn with_sheet_type(mut self) -> Self {
104 self.request.file_type = "sheet".to_string();
105 self
106 }
107
108 pub fn with_bitable_type(mut self) -> Self {
110 self.request.file_type = "bitable".to_string();
111 self
112 }
113
114 pub fn content(mut self, content: ReplyContent) -> Self {
116 self.request.content = content;
117 self
118 }
119
120 pub fn text(mut self, text: impl ToString) -> Self {
122 let element = ContentElement {
123 element_type: "text_run".to_string(),
124 text_run: Some(TextRun {
125 text: text.to_string(),
126 style: None,
127 }),
128 };
129 self.request.content.elements.push(element);
130 self
131 }
132
133 pub fn styled_text(mut self, text: impl ToString, style: serde_json::Value) -> Self {
135 let element = ContentElement {
136 element_type: "text_run".to_string(),
137 text_run: Some(TextRun {
138 text: text.to_string(),
139 style: Some(style),
140 }),
141 };
142 self.request.content.elements.push(element);
143 self
144 }
145
146 pub fn bold_text(self, text: impl ToString) -> Self {
148 let style = serde_json::json!({
149 "bold": true
150 });
151 self.styled_text(text, style)
152 }
153
154 pub fn italic_text(self, text: impl ToString) -> Self {
156 let style = serde_json::json!({
157 "italic": true
158 });
159 self.styled_text(text, style)
160 }
161
162 pub fn user_id_type(mut self, user_id_type: impl ToString) -> Self {
164 self.request.user_id_type = Some(user_id_type.to_string());
165 self
166 }
167
168 pub fn with_open_id(mut self) -> Self {
170 self.request.user_id_type = Some("open_id".to_string());
171 self
172 }
173
174 pub fn with_user_id(mut self) -> Self {
176 self.request.user_id_type = Some("user_id".to_string());
177 self
178 }
179
180 pub fn with_union_id(mut self) -> Self {
182 self.request.user_id_type = Some("union_id".to_string());
183 self
184 }
185
186 pub fn build(mut self) -> CreateCommentRequest {
187 self.request.api_request.body = serde_json::to_vec(&self.request).unwrap();
188 self.request
189 }
190}
191
192#[derive(Debug, Deserialize)]
194pub struct CreatedComment {
195 pub comment_id: String,
197 pub user_id: String,
199 pub create_time: i64,
201 pub update_time: i64,
203 pub is_solved: bool,
205 pub is_whole: Option<bool>,
207}
208
209impl_executable_builder_owned!(
211 CreateCommentRequestBuilder,
212 super::CommentsService,
213 CreateCommentRequest,
214 BaseResponse<CreateCommentResponse>,
215 create
216);
217
218#[derive(Debug, Deserialize)]
220pub struct CreateCommentResponse {
221 pub comment: CreatedComment,
223}
224
225impl ApiResponseTrait for CreateCommentResponse {
226 fn data_format() -> ResponseFormat {
227 ResponseFormat::Data
228 }
229}
230
231pub async fn create_comment(
235 request: CreateCommentRequest,
236 config: &Config,
237 option: Option<RequestOption>,
238) -> SDKResult<BaseResponse<CreateCommentResponse>> {
239 let mut api_req = request.api_request;
240 api_req.http_method = Method::POST;
241 api_req.api_path = format!(
242 "{}?file_type={}&file_token={}",
243 COMMENT_V1_COMMENTS, request.file_type, request.file_token
244 );
245
246 if let Some(user_id_type) = request.user_id_type {
248 api_req.api_path = format!("{}&user_id_type={}", api_req.api_path, user_id_type);
249 }
250
251 api_req.supported_access_token_types = vec![AccessTokenType::Tenant, AccessTokenType::User];
252
253 let api_resp = Transport::request(api_req, config, option).await?;
254 Ok(api_resp)
255}
256
257impl CreatedComment {
258 pub fn is_whole_comment(&self) -> bool {
260 self.is_whole.unwrap_or(false)
261 }
262
263 pub fn created_at_formatted(&self) -> String {
265 format!("创建时间: {}", self.create_time)
266 }
267
268 pub fn is_solved(&self) -> bool {
270 self.is_solved
271 }
272}
273
274pub struct ContentBuilder {
276 elements: Vec<ContentElement>,
277}
278
279impl ContentBuilder {
280 pub fn new() -> Self {
281 Self {
282 elements: Vec::new(),
283 }
284 }
285
286 pub fn add_text(mut self, text: impl ToString) -> Self {
288 self.elements.push(ContentElement {
289 element_type: "text_run".to_string(),
290 text_run: Some(TextRun {
291 text: text.to_string(),
292 style: None,
293 }),
294 });
295 self
296 }
297
298 pub fn add_styled_text(mut self, text: impl ToString, style: serde_json::Value) -> Self {
300 self.elements.push(ContentElement {
301 element_type: "text_run".to_string(),
302 text_run: Some(TextRun {
303 text: text.to_string(),
304 style: Some(style),
305 }),
306 });
307 self
308 }
309
310 pub fn add_bold(self, text: impl ToString) -> Self {
312 let style = serde_json::json!({ "bold": true });
313 self.add_styled_text(text, style)
314 }
315
316 pub fn add_italic(self, text: impl ToString) -> Self {
318 let style = serde_json::json!({ "italic": true });
319 self.add_styled_text(text, style)
320 }
321
322 pub fn add_underline(self, text: impl ToString) -> Self {
324 let style = serde_json::json!({ "underline": true });
325 self.add_styled_text(text, style)
326 }
327
328 pub fn build(self) -> ReplyContent {
330 ReplyContent {
331 elements: self.elements,
332 }
333 }
334}
335
336impl Default for ContentBuilder {
337 fn default() -> Self {
338 Self::new()
339 }
340}
341
342#[cfg(test)]
343#[allow(unused_variables, unused_unsafe)]
344mod tests {
345 use super::*;
346
347 #[test]
348 fn test_create_comment_request_builder() {
349 let request = CreateCommentRequest::builder()
350 .file_token("doccnxxxxxx")
351 .with_doc_type()
352 .text("这是一条评论")
353 .bold_text("重要内容")
354 .with_open_id()
355 .build();
356
357 assert_eq!(request.file_token, "doccnxxxxxx");
358 assert_eq!(request.file_type, "doc");
359 assert_eq!(request.content.elements.len(), 2);
360 assert_eq!(request.user_id_type, Some("open_id".to_string()));
361 }
362
363 #[test]
364 fn test_create_comment_with_text() {
365 let request = CreateCommentRequest::with_text("doccnxxxxxx", "doc", "简单评论");
366 assert_eq!(request.file_token, "doccnxxxxxx");
367 assert_eq!(request.file_type, "doc");
368 assert_eq!(request.content.elements.len(), 1);
369 }
370
371 #[test]
372 fn test_content_builder() {
373 let content = ContentBuilder::new()
374 .add_text("普通文本 ")
375 .add_bold("粗体文本 ")
376 .add_italic("斜体文本")
377 .build();
378
379 assert_eq!(content.elements.len(), 3);
380 assert_eq!(content.elements[0].element_type, "text_run");
381 }
382}