use async_trait::async_trait;
use serde_json::Value;
use synaptic_core::{Document, Loader, SynapticError};
use crate::{
auth::TokenCache, loaders::doc::LarkDocLoader, loaders::spreadsheet::LarkSpreadsheetLoader,
LarkConfig,
};
pub struct LarkDriveLoader {
token_cache: TokenCache,
base_url: String,
config_snapshot: LarkConfig,
client: reqwest::Client,
folder_token: Option<String>,
recursive: bool,
}
impl LarkDriveLoader {
pub fn new(config: LarkConfig) -> Self {
let base_url = config.base_url.clone();
Self {
token_cache: config.clone().token_cache(),
base_url,
config_snapshot: config,
client: reqwest::Client::new(),
folder_token: None,
recursive: false,
}
}
pub fn with_folder_token(mut self, t: impl Into<String>) -> Self {
self.folder_token = Some(t.into());
self
}
pub fn recursive(mut self) -> Self {
self.recursive = true;
self
}
pub fn folder_token(&self) -> &str {
self.folder_token.as_deref().unwrap_or("")
}
}
#[async_trait]
impl Loader for LarkDriveLoader {
async fn load(&self) -> Result<Vec<Document>, SynapticError> {
let folder = self.folder_token.as_deref().ok_or_else(|| {
SynapticError::Config("LarkDriveLoader: folder_token not set".to_string())
})?;
let token = self.token_cache.get_token().await?;
let url = format!(
"{}/drive/v1/files?folder_token={}&page_size=200",
self.base_url, folder
);
let resp = self
.client
.get(&url)
.bearer_auth(&token)
.send()
.await
.map_err(|e| SynapticError::Loader(format!("drive list: {e}")))?;
let body: Value = resp
.json()
.await
.map_err(|e| SynapticError::Loader(format!("drive list parse: {e}")))?;
if body["code"].as_i64().unwrap_or(-1) != 0 {
return Err(SynapticError::Loader(format!(
"Lark Drive API error: {}",
body["msg"].as_str().unwrap_or("unknown")
)));
}
let mut docs = Vec::new();
let items = body["data"]["files"]
.as_array()
.cloned()
.unwrap_or_default();
for item in &items {
let file_type = item["type"].as_str().unwrap_or("");
let token_val = item["token"].as_str().unwrap_or("");
match file_type {
"doc" | "docx" => {
let loader = LarkDocLoader::new(self.config_snapshot.clone())
.with_doc_tokens(vec![token_val.to_string()]);
match loader.load().await {
Ok(d) => docs.extend(d),
Err(e) => tracing::warn!("drive: skip doc {token_val}: {e}"),
}
}
"sheet" => {
let loader = LarkSpreadsheetLoader::new(self.config_snapshot.clone())
.with_token(token_val);
match loader.load().await {
Ok(d) => docs.extend(d),
Err(e) => tracing::warn!("drive: skip sheet {token_val}: {e}"),
}
}
"folder" if self.recursive => {
let sub = LarkDriveLoader::new(self.config_snapshot.clone())
.with_folder_token(token_val)
.recursive();
match sub.load().await {
Ok(d) => docs.extend(d),
Err(e) => tracing::warn!("drive: skip subfolder {token_val}: {e}"),
}
}
_ => {}
}
}
Ok(docs)
}
}