use crate::utils::encode_path;
use crate::{FileEntity, FilesClient, PaginationInfo, Result};
use futures::stream::Stream;
use serde_json::json;
#[derive(Debug, Clone)]
pub struct FolderHandler {
client: FilesClient,
}
impl FolderHandler {
pub fn new(client: FilesClient) -> Self {
Self { client }
}
pub async fn list_folder(
&self,
path: &str,
per_page: Option<i32>,
cursor: Option<String>,
) -> Result<(Vec<FileEntity>, PaginationInfo)> {
let encoded_path = encode_path(path);
let mut endpoint = format!("/folders{}", encoded_path);
let mut query_params = Vec::new();
if let Some(per_page) = per_page {
query_params.push(format!("per_page={}", per_page));
}
if let Some(cursor) = cursor {
query_params.push(format!("cursor={}", cursor));
}
if !query_params.is_empty() {
endpoint.push('?');
endpoint.push_str(&query_params.join("&"));
}
let url = format!("{}{}", self.client.inner.base_url, endpoint);
let response = reqwest::Client::new()
.get(&url)
.header("X-FilesAPI-Key", &self.client.inner.api_key)
.send()
.await?;
let headers = response.headers().clone();
let pagination = PaginationInfo::from_headers(&headers);
let status = response.status();
if !status.is_success() {
return Err(crate::FilesError::ApiError {
endpoint: None,
code: status.as_u16(),
message: response.text().await.unwrap_or_default(),
});
}
let files: Vec<FileEntity> = response.json().await?;
Ok((files, pagination))
}
pub async fn list_folder_all(&self, path: &str) -> Result<Vec<FileEntity>> {
let mut all_files = Vec::new();
let mut cursor = None;
loop {
let (mut files, pagination) = self.list_folder(path, Some(1000), cursor).await?;
all_files.append(&mut files);
if pagination.has_next() {
cursor = pagination.cursor_next;
} else {
break;
}
}
Ok(all_files)
}
pub fn list_stream(
&self,
path: &str,
per_page: Option<i32>,
) -> impl Stream<Item = Result<FileEntity>> + '_ {
let path = path.to_string();
let per_page = per_page.unwrap_or(1000);
async_stream::try_stream! {
let mut cursor: Option<String> = None;
loop {
let (files, pagination) = self
.list_folder(&path, Some(per_page), cursor.clone())
.await?;
for file in files {
yield file;
}
match pagination.cursor_next {
Some(next) => cursor = Some(next),
None => break,
}
}
}
}
pub async fn create_folder(&self, path: &str, mkdir_parents: bool) -> Result<FileEntity> {
let body = json!({
"path": path,
"mkdir_parents": mkdir_parents,
});
let encoded_path = encode_path(path);
let endpoint = format!("/folders{}", encoded_path);
let response = self.client.post_raw(&endpoint, body).await?;
Ok(serde_json::from_value(response)?)
}
pub async fn delete_folder(&self, path: &str, recursive: bool) -> Result<()> {
let encoded_path = encode_path(path);
let endpoint = if recursive {
format!("/folders{}?recursive=true", encoded_path)
} else {
format!("/folders{}", encoded_path)
};
self.client.delete_raw(&endpoint).await?;
Ok(())
}
pub async fn search_folder(
&self,
path: &str,
search: &str,
per_page: Option<i32>,
) -> Result<(Vec<FileEntity>, PaginationInfo)> {
let encoded_path = encode_path(path);
let mut endpoint = format!("/folders{}?search={}", encoded_path, search);
if let Some(per_page) = per_page {
endpoint.push_str(&format!("&per_page={}", per_page));
}
let url = format!("{}{}", self.client.inner.base_url, endpoint);
let response = reqwest::Client::new()
.get(&url)
.header("X-FilesAPI-Key", &self.client.inner.api_key)
.send()
.await?;
let headers = response.headers().clone();
let pagination = PaginationInfo::from_headers(&headers);
let status = response.status();
if !status.is_success() {
return Err(crate::FilesError::ApiError {
endpoint: None,
code: status.as_u16(),
message: response.text().await.unwrap_or_default(),
});
}
let files: Vec<FileEntity> = response.json().await?;
Ok((files, pagination))
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_handler_creation() {
let client = FilesClient::builder().api_key("test-key").build().unwrap();
let _handler = FolderHandler::new(client);
}
}