open_lark/service/search/v1/
user.rs1use log::error;
2use reqwest::Method;
3use serde::{Deserialize, Serialize};
4
5use crate::core::{
6 api_req::ApiRequest,
7 api_resp::{ApiResponseTrait, BaseResponse},
8 config::Config,
9 constants::AccessTokenType,
10 http::Transport,
11 req_option::RequestOption,
12 standard_response::StandardResponse,
13 validation::{self, ValidationResult},
14 SDKResult,
15};
16use crate::impl_full_service;
17
18pub struct UserService {
19 config: Config,
20}
21
22impl UserService {
23 pub fn new(config: Config) -> Self {
24 Self { config }
25 }
26
27 pub async fn search_user(
31 &self,
32 search_user_request: SearchUserRequest,
33 option: Option<RequestOption>,
34 ) -> SDKResult<SearchUserResponse> {
35 let mut api_req = search_user_request.api_request;
36 api_req.http_method = Method::GET;
37 api_req.api_path = crate::core::endpoints::search::SEARCH_V1_USER.to_string();
38 api_req.supported_access_token_types = vec![AccessTokenType::User];
39
40 let api_resp: BaseResponse<SearchUserResponse> =
41 Transport::request(api_req, &self.config, option).await?;
42 api_resp.into_result()
43 }
44
45 async fn search_user_with_base_response(
47 &self,
48 search_user_request: SearchUserRequest,
49 option: Option<RequestOption>,
50 ) -> SDKResult<BaseResponse<SearchUserResponse>> {
51 let mut api_req = search_user_request.api_request;
52 api_req.http_method = Method::GET;
53 api_req.api_path = crate::core::endpoints::search::SEARCH_V1_USER.to_string();
54 api_req.supported_access_token_types = vec![AccessTokenType::User];
55
56 Transport::request(api_req, &self.config, option).await
57 }
58
59 pub fn search_user_iter(
60 &self,
61 search_user_request: SearchUserRequest,
62 option: Option<RequestOption>,
63 ) -> SearchUserIterator<'_> {
64 SearchUserIterator {
65 user_service: self,
66 request: search_user_request,
67 option,
68 has_more: true,
69 }
70 }
71
72 pub async fn search_user_with_validated_pagination(
76 &self,
77 query: impl ToString,
78 page_size: Option<u32>,
79 page_token: Option<String>,
80 option: Option<RequestOption>,
81 ) -> SDKResult<SearchUserResponse> {
82 let builder = SearchUserRequest::builder()
84 .query(query)
85 .with_pagination(page_size, page_token)?;
86
87 self.search_user(builder.build(), option).await
88 }
89}
90
91impl_full_service!(UserService, "search.user", "v1");
92
93#[derive(Default, Clone)]
95pub struct SearchUserRequest {
96 api_request: ApiRequest,
97}
98
99impl SearchUserRequest {
100 pub fn builder() -> SearchUserRequestBuilder {
101 SearchUserRequestBuilder::default()
102 }
103}
104
105#[derive(Default)]
106pub struct SearchUserRequestBuilder {
107 search_user_request: SearchUserRequest,
108}
109
110impl SearchUserRequestBuilder {
111 pub fn query(mut self, query: impl ToString) -> Self {
113 self.search_user_request
114 .api_request
115 .query_params
116 .insert("query", query.to_string());
117 self
118 }
119
120 pub fn page_size(mut self, page_size: i32) -> Self {
126 if !(1..=200).contains(&page_size) {
128 log::warn!(
129 "Page size {} is out of valid range (1-200) for search service",
130 page_size
131 );
132 }
133
134 self.search_user_request
135 .api_request
136 .query_params
137 .insert("page_size", page_size.to_string());
138 self
139 }
140
141 pub fn page_token(mut self, page_token: impl ToString) -> Self {
144 let token = page_token.to_string();
145
146 match validation::validate_page_token(&token, "page_token") {
148 ValidationResult::Valid => {}
149 ValidationResult::Warning(msg) => {
150 log::warn!("Page token validation warning: {}", msg);
151 }
152 ValidationResult::Invalid(msg) => {
153 log::error!("Invalid page token: {}", msg);
154 }
155 }
156
157 self.search_user_request
158 .api_request
159 .query_params
160 .insert("page_token", token);
161 self
162 }
163
164 pub fn build(self) -> SearchUserRequest {
165 self.search_user_request
166 }
167
168 pub fn with_pagination(
173 mut self,
174 page_size: Option<u32>,
175 page_token: Option<String>,
176 ) -> SDKResult<Self> {
177 let mut pagination_builder =
178 validation::pagination::PaginationRequestBuilder::<SearchUserResponse>::new();
179
180 if let Some(size) = page_size {
181 if size > 200 {
183 return Err(crate::core::error::LarkAPIError::illegal_param(format!(
184 "Page size {} exceeds maximum limit of 200 for search service",
185 size
186 )));
187 }
188 pagination_builder = pagination_builder.with_page_size(size);
189 }
190
191 if let Some(token) = page_token {
192 pagination_builder = pagination_builder.with_page_token(token);
193 }
194
195 let params = pagination_builder.build()?;
197
198 for (key, value) in params {
200 self.search_user_request
201 .api_request
202 .query_params
203 .insert(key, value);
204 }
205
206 Ok(self)
207 }
208}
209
210crate::impl_executable_builder_owned!(
211 SearchUserRequestBuilder,
212 UserService,
213 SearchUserRequest,
214 SearchUserResponse,
215 search_user
216);
217
218#[derive(Debug, Serialize, Deserialize)]
219pub struct SearchUserResponse {
220 pub users: Vec<UserInSearchResponse>,
222 pub has_more: bool,
224 #[serde(skip_serializing_if = "Option::is_none")]
226 pub page_token: Option<String>,
227}
228
229#[derive(Debug, Serialize, Deserialize)]
231pub struct UserInSearchResponse {
232 pub avatar: UserAvatar,
234 pub department_ids: Vec<String>,
236 pub name: String,
238 pub open_id: String,
240 #[serde(skip_serializing_if = "Option::is_none")]
242 pub user_id: Option<String>,
243}
244
245#[derive(Debug, Serialize, Deserialize)]
247pub struct UserAvatar {
248 pub avatar_72: String,
250 pub avatar_240: String,
252 pub avatar_640: String,
254 pub avatar_origin: String,
256}
257
258impl ApiResponseTrait for SearchUserResponse {
259 fn data_format() -> crate::core::api_resp::ResponseFormat {
260 crate::core::api_resp::ResponseFormat::Data
261 }
262}
263
264pub struct SearchUserIterator<'a> {
265 user_service: &'a UserService,
266 request: SearchUserRequest,
267 option: Option<RequestOption>,
268 has_more: bool,
269}
270
271impl SearchUserIterator<'_> {
272 pub async fn next(&mut self) -> Option<Vec<UserInSearchResponse>> {
273 if !self.has_more {
274 return None;
275 }
276
277 match self
278 .user_service
279 .search_user_with_base_response(self.request.clone(), self.option.clone())
280 .await
281 {
282 Ok(resp) => match resp.data {
283 Some(data) => {
284 self.has_more = data.has_more;
285 if data.has_more {
286 if let Some(token) = data.page_token {
287 self.request
288 .api_request
289 .query_params
290 .insert("page_token", token);
291 Some(data.users)
292 } else {
293 self.has_more = false;
295 Some(data.users)
296 }
297 } else if data.users.is_empty() {
298 None
299 } else {
300 Some(data.users)
301 }
302 }
303 None => None,
304 },
305 Err(e) => {
306 error!("Error: {e:?}");
307 None
308 }
309 }
310 }
311}