use openlark_core::api::{ApiResponseTrait, ResponseFormat};
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SearchDocsRequest {
pub search_key: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub doc_types: Option<Vec<String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub search_scope: Option<SearchScope>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sort: Option<SortRule>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_size: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "snake_case")]
pub enum SearchScope {
CreatedByMe,
SharedWithMe,
Starred,
RecentlyViewed,
Folder(String),
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct SortRule {
pub sort_field: String,
pub sort_direction: SortDirection,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
#[serde(rename_all = "lowercase")]
pub enum SortDirection {
Asc,
Desc,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct SearchDocsResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Vec<DocumentItem>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_more: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
}
impl ApiResponseTrait for SearchDocsResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub doc_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub create_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modify_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub creator: Option<UserInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<UserInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_starred: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<DocumentPermissions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UserInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub user_id_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub avatar: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentPermissions {
#[serde(skip_serializing_if = "Option::is_none")]
pub can_view: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_edit: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_comment: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_share: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub can_download: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GetDocMetaRequest {
pub tokens: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra_fields: Option<Vec<String>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct GetDocMetaResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub items: Option<Vec<DocumentMetaItem>>,
}
impl ApiResponseTrait for GetDocMetaResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentMetaItem {
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub basic_info: Option<DocumentBasicInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub statistics: Option<DocumentStatistics>,
#[serde(skip_serializing_if = "Option::is_none")]
pub extra: Option<HashMap<String, serde_json::Value>>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentBasicInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub doc_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub create_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub modify_time: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub creator: Option<UserInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub owner: Option<UserInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent: Option<FolderInfo>,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<DocumentPermissions>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct FolderInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub name: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentStatistics {
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub word_count: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub row_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub slide_count: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub node_count: Option<i32>,
}
impl SearchDocsRequest {
pub fn validate(&self) -> Result<(), String> {
if self.search_key.trim().is_empty() {
return Err("搜索关键字不能为空".to_string());
}
if self.search_key.len() > 1000 {
return Err("搜索关键字长度不能超过1000个字符".to_string());
}
if let Some(page_size) = self.page_size {
if page_size < 1 || page_size > 100 {
return Err("每页数量必须在1-100之间".to_string());
}
}
if let Some(ref doc_types) = self.doc_types {
if doc_types.len() > 10 {
return Err("文档类型列表长度不能超过10".to_string());
}
for doc_type in doc_types {
if !Self::is_valid_doc_type(doc_type) {
return Err(format!("不支持的文档类型: {}", doc_type));
}
}
}
Ok(())
}
fn is_valid_doc_type(doc_type: &str) -> bool {
matches!(
doc_type,
"doc" | "sheet" | "slide" | "mindnote" | "docx" | "bitable" | "file"
)
}
}
impl GetDocMetaRequest {
pub fn validate(&self) -> Result<(), String> {
if self.tokens.is_empty() {
return Err("文档token列表不能为空".to_string());
}
if self.tokens.len() > 100 {
return Err("文档token列表长度不能超过100".to_string());
}
for token in &self.tokens {
if token.trim().is_empty() {
return Err("文档token不能为空".to_string());
}
}
Ok(())
}
}
pub mod common_types {
use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockContent {
pub text: Option<String>,
pub rich_text: Option<RichText>,
#[serde(flatten)]
pub other: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct RichText {
pub paragraphs: Vec<Paragraph>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct Paragraph {
pub elements: Vec<TextElement>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextElement {
pub text_run: Option<TextRun>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextRun {
pub content: String,
pub style: Option<TextStyle>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TextStyle {
pub bold: Option<bool>,
pub italic: Option<bool>,
pub strikethrough: Option<bool>,
pub font_size: Option<u32>,
pub font_color: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockItem {
pub block_id: String,
pub block_type: String,
pub content: Option<BlockContent>,
pub children_count: Option<u32>,
pub create_time: Option<i64>,
pub update_time: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct PageData<T> {
pub items: Vec<T>,
pub page_token: Option<String>,
pub has_more: Option<bool>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BlockUpdate {
pub block_id: String,
pub content: Option<BlockContent>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct BatchOperationResult {
pub success_count: Option<u32>,
pub failed_count: Option<u32>,
pub failed_items: Option<Vec<FailedItem>>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FailedItem {
pub item_id: String,
pub error_message: String,
pub error_code: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DocxBlock {
pub block_id: String,
pub block_type: i32,
#[serde(default)]
pub children: Vec<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_id: Option<String>,
#[serde(default, flatten)]
pub extra: HashMap<String, serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, Default)]
pub struct DocxBlockPage {
#[serde(default)]
pub items: Vec<DocxBlock>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_more: Option<bool>,
}
}
pub mod models_docx {
use crate::prelude::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DocumentInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub document_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub revision_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BlockInfo {
#[serde(skip_serializing_if = "Option::is_none")]
pub block_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub block_type: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CreateDocumentRequest {
pub title: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub folder_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parent_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub cover_key: Option<String>,
}
impl CreateDocumentRequest {
pub fn validate(&self) -> Result<(), String> {
if self.title.trim().is_empty() {
return Err("文档标题不能为空".to_string());
}
if self.title.len() > 100 {
return Err("文档标题长度不能超过100个字符".to_string());
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct CreateDocumentResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub document_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub document: Option<DocumentInfo>,
}
impl ApiResponseTrait for CreateDocumentResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GetDocumentRequest {
pub document_token: String,
}
impl GetDocumentRequest {
pub fn validate(&self) -> Result<(), String> {
if self.document_token.trim().is_empty() {
return Err("文档token不能为空".to_string());
}
Ok(())
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct GetDocumentResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub document: Option<DocumentInfo>,
}
impl ApiResponseTrait for GetDocumentResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GetRawContentRequest {
pub document_token: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct GetRawContentResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<String>,
}
impl ApiResponseTrait for GetRawContentResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct CreateBlockRequest {
pub document_token: String,
pub parent_block_id: String,
pub block_type: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub content: Option<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct CreateBlockResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub block: Option<BlockInfo>,
}
impl ApiResponseTrait for CreateBlockResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct GetBlockRequest {
pub document_token: String,
pub block_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct GetBlockResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub block: Option<BlockInfo>,
}
impl ApiResponseTrait for GetBlockResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct ListBlocksRequest {
pub document_token: String,
pub parent_block_id: String,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_size: Option<i32>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct ListBlocksResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub blocks: Option<Vec<BlockInfo>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub page_token: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub has_more: Option<bool>,
}
impl ApiResponseTrait for ListBlocksResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct UpdateBlockRequest {
pub document_token: String,
pub block_id: String,
pub content: serde_json::Value,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct UpdateBlockResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub block: Option<BlockInfo>,
}
impl ApiResponseTrait for UpdateBlockResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BatchUpdateBlocksRequest {
pub document_token: String,
pub requests: Vec<serde_json::Value>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct BatchUpdateBlocksResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub results: Option<Vec<serde_json::Value>>,
}
impl ApiResponseTrait for BatchUpdateBlocksResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct DeleteBlockRequest {
pub document_token: String,
pub block_id: String,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct DeleteBlockResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub deleted: Option<bool>,
}
impl ApiResponseTrait for DeleteBlockResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
pub struct BatchDeleteBlocksRequest {
pub document_token: String,
pub block_ids: Vec<String>,
}
#[derive(Debug, Clone, Serialize, Deserialize, PartialEq, Default)]
pub struct BatchDeleteBlocksResponse {
#[serde(skip_serializing_if = "Option::is_none")]
pub results: Option<Vec<serde_json::Value>>,
}
impl ApiResponseTrait for BatchDeleteBlocksResponse {
fn data_format() -> ResponseFormat {
ResponseFormat::Data
}
}
}
#[cfg(test)]
mod tests {
use serde_json;
#[test]
fn test_serialization_roundtrip() {
let json = r#"{"test": "value"}"#;
assert!(serde_json::from_str::<serde_json::Value>(json).is_ok());
}
#[test]
fn test_deserialization_from_json() {
let json = r#"{"field": "data"}"#;
let value: serde_json::Value = serde_json::from_str(json).unwrap();
assert_eq!(value["field"], "data");
}
}