use log::error;
use reqwest::Method;
use serde::{Deserialize, Serialize};
use crate::core::{
api_req::ApiRequest,
api_resp::{ApiResponseTrait, BaseResponse},
config::Config,
constants::AccessTokenType,
http::Transport,
req_option::RequestOption,
standard_response::StandardResponse,
validation::{self, ValidationResult},
SDKResult,
};
use crate::impl_full_service;
pub struct UserService {
config: Config,
}
impl UserService {
pub fn new(config: Config) -> Self {
Self { config }
}
pub async fn search_user(
&self,
search_user_request: SearchUserRequest,
option: Option<RequestOption>,
) -> SDKResult<SearchUserResponse> {
let mut api_req = search_user_request.api_request;
api_req.http_method = Method::GET;
api_req.api_path = crate::core::endpoints::search::SEARCH_V1_USER.to_string();
api_req.supported_access_token_types = vec![AccessTokenType::User];
let api_resp: BaseResponse<SearchUserResponse> =
Transport::request(api_req, &self.config, option).await?;
api_resp.into_result()
}
async fn search_user_with_base_response(
&self,
search_user_request: SearchUserRequest,
option: Option<RequestOption>,
) -> SDKResult<BaseResponse<SearchUserResponse>> {
let mut api_req = search_user_request.api_request;
api_req.http_method = Method::GET;
api_req.api_path = crate::core::endpoints::search::SEARCH_V1_USER.to_string();
api_req.supported_access_token_types = vec![AccessTokenType::User];
Transport::request(api_req, &self.config, option).await
}
pub fn search_user_iter(
&self,
search_user_request: SearchUserRequest,
option: Option<RequestOption>,
) -> SearchUserIterator<'_> {
SearchUserIterator {
user_service: self,
request: search_user_request,
option,
has_more: true,
}
}
pub async fn search_user_with_validated_pagination(
&self,
query: impl ToString,
page_size: Option<u32>,
page_token: Option<String>,
option: Option<RequestOption>,
) -> SDKResult<SearchUserResponse> {
let builder = SearchUserRequest::builder()
.query(query)
.with_pagination(page_size, page_token)?;
self.search_user(builder.build(), option).await
}
}
impl_full_service!(UserService, "search.user", "v1");
#[derive(Default, Clone)]
pub struct SearchUserRequest {
api_request: ApiRequest,
}
impl SearchUserRequest {
pub fn builder() -> SearchUserRequestBuilder {
SearchUserRequestBuilder::default()
}
}
#[derive(Default)]
pub struct SearchUserRequestBuilder {
search_user_request: SearchUserRequest,
}
impl SearchUserRequestBuilder {
pub fn query(mut self, query: impl ToString) -> Self {
self.search_user_request
.api_request
.query_params
.insert("query", query.to_string());
self
}
pub fn page_size(mut self, page_size: i32) -> Self {
if !(1..=200).contains(&page_size) {
log::warn!(
"Page size {} is out of valid range (1-200) for search service",
page_size
);
}
self.search_user_request
.api_request
.query_params
.insert("page_size", page_size.to_string());
self
}
pub fn page_token(mut self, page_token: impl ToString) -> Self {
let token = page_token.to_string();
match validation::validate_page_token(&token, "page_token") {
ValidationResult::Valid => {}
ValidationResult::Warning(msg) => {
log::warn!("Page token validation warning: {}", msg);
}
ValidationResult::Invalid(msg) => {
log::error!("Invalid page token: {}", msg);
}
}
self.search_user_request
.api_request
.query_params
.insert("page_token", token);
self
}
pub fn build(self) -> SearchUserRequest {
self.search_user_request
}
pub fn with_pagination(
mut self,
page_size: Option<u32>,
page_token: Option<String>,
) -> SDKResult<Self> {
let mut pagination_builder =
validation::pagination::PaginationRequestBuilder::<SearchUserResponse>::new();
if let Some(size) = page_size {
if size > 200 {
return Err(crate::core::error::LarkAPIError::illegal_param(format!(
"Page size {} exceeds maximum limit of 200 for search service",
size
)));
}
pagination_builder = pagination_builder.with_page_size(size);
}
if let Some(token) = page_token {
pagination_builder = pagination_builder.with_page_token(token);
}
let params = pagination_builder.build()?;
for (key, value) in params {
self.search_user_request
.api_request
.query_params
.insert(key, value);
}
Ok(self)
}
}
crate::impl_executable_builder_owned!(
SearchUserRequestBuilder,
UserService,
SearchUserRequest,
SearchUserResponse,
search_user
);
#[derive(Debug, Serialize, Deserialize)]
pub struct SearchUserResponse {
pub users: Vec<UserInSearchResponse>,
pub has_more: bool,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UserInSearchResponse {
pub avatar: UserAvatar,
pub department_ids: Vec<String>,
pub name: String,
pub open_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
}
#[derive(Debug, Serialize, Deserialize)]
pub struct UserAvatar {
pub avatar_72: String,
pub avatar_240: String,
pub avatar_640: String,
pub avatar_origin: String,
}
impl ApiResponseTrait for SearchUserResponse {
fn data_format() -> crate::core::api_resp::ResponseFormat {
crate::core::api_resp::ResponseFormat::Data
}
}
pub struct SearchUserIterator<'a> {
user_service: &'a UserService,
request: SearchUserRequest,
option: Option<RequestOption>,
has_more: bool,
}
impl SearchUserIterator<'_> {
pub async fn next(&mut self) -> Option<Vec<UserInSearchResponse>> {
if !self.has_more {
return None;
}
match self
.user_service
.search_user_with_base_response(self.request.clone(), self.option.clone())
.await
{
Ok(resp) => match resp.data {
Some(data) => {
self.has_more = data.has_more;
if data.has_more {
if let Some(token) = data.page_token {
self.request
.api_request
.query_params
.insert("page_token", token);
Some(data.users)
} else {
self.has_more = false;
Some(data.users)
}
} else if data.users.is_empty() {
None
} else {
Some(data.users)
}
}
None => None,
},
Err(e) => {
error!("Error: {e:?}");
None
}
}
}
}