Files
ente/rust/src/api/methods.rs
Manav Rathi d4ae8d63fc fix(rust): Fix linting and formatting issues for CI
- Applied cargo fmt to ensure consistent formatting
- Fixed all clippy warnings (uninlined_format_args)
- Code now passes all CI checks with RUSTFLAGS="-D warnings"

Co-Authored-By: Claude <noreply@anthropic.com>
2025-08-23 09:01:56 +05:30

178 lines
6.3 KiB
Rust

use crate::api::client::ApiClient;
use crate::api::models::{
Collection, File, GetCollectionsResponse, GetDiffResponse, GetFileResponse, GetFilesResponse,
GetThumbnailUrlResponse, UserDetails,
};
use crate::models::error::Result;
/// API methods for interacting with Ente services
pub struct ApiMethods<'a> {
api: &'a ApiClient,
}
impl<'a> ApiMethods<'a> {
pub fn new(api: &'a ApiClient) -> Self {
Self { api }
}
// ========== User Methods ==========
/// Get user details including subscription and storage info
pub async fn get_user_details(&self, account_id: &str) -> Result<UserDetails> {
self.api.get("/users/details", Some(account_id)).await
}
// ========== Collection Methods ==========
/// Get all collections (albums) for the authenticated user
///
/// # Arguments
/// * `account_id` - The account identifier for authentication
/// * `since_time` - Unix timestamp in microseconds to get collections modified after this time (0 for all)
pub async fn get_collections(
&self,
account_id: &str,
since_time: i64,
) -> Result<Vec<Collection>> {
let url = format!("/collections/v2?sinceTime={since_time}");
let response: GetCollectionsResponse = self.api.get(&url, Some(account_id)).await?;
Ok(response.collections)
}
/// Get a specific collection by ID
pub async fn get_collection(&self, account_id: &str, collection_id: i64) -> Result<Collection> {
let url = format!("/collections/{collection_id}");
self.api.get(&url, Some(account_id)).await
}
// ========== File Methods ==========
/// Get files from a specific collection with pagination
///
/// # Arguments
/// * `account_id` - The account identifier for authentication
/// * `collection_id` - The collection ID to fetch files from
/// * `since_time` - Unix timestamp in microseconds to get files modified after this time
///
/// # Returns
/// A tuple of (files, has_more) where has_more indicates if there are more files to fetch
pub async fn get_collection_files(
&self,
account_id: &str,
collection_id: i64,
since_time: i64,
) -> Result<(Vec<File>, bool)> {
let url =
format!("/collections/v2/diff?collectionID={collection_id}&sinceTime={since_time}");
let response: GetFilesResponse = self.api.get(&url, Some(account_id)).await?;
Ok((response.diff, response.has_more))
}
/// Get a specific file by ID
pub async fn get_file(
&self,
account_id: &str,
collection_id: i64,
file_id: i64,
) -> Result<File> {
let url = format!("/collections/file?collectionID={collection_id}&fileID={file_id}");
let response: GetFileResponse = self.api.get(&url, Some(account_id)).await?;
Ok(response.file)
}
/// Get all files across all collections (for incremental sync)
///
/// # Arguments
/// * `account_id` - The account identifier for authentication
/// * `since_time` - Unix timestamp in microseconds to get files modified after this time
/// * `limit` - Maximum number of files to return (typically 500)
///
/// # Returns
/// A tuple of (files, has_more) where has_more indicates if there are more files to fetch
pub async fn get_diff(
&self,
account_id: &str,
since_time: i64,
limit: i32,
) -> Result<(Vec<File>, bool)> {
let url = format!("/diff?sinceTime={since_time}&limit={limit}");
let response: GetDiffResponse = self.api.get(&url, Some(account_id)).await?;
Ok((response.diff, response.has_more))
}
/// Get download URL for a file
pub async fn get_file_url(&self, _account_id: &str, file_id: i64) -> Result<String> {
// Check if we're using the default API endpoint
let base_url = &self.api.base_url;
if base_url == "https://api.ente.io" {
// Use the CDN URL for production
Ok(format!("https://files.ente.io/?fileID={file_id}"))
} else {
// For custom/dev environments, use direct download URL
// The Go implementation shows this is the pattern
Ok(format!("{base_url}/files/download/{file_id}"))
}
}
/// Get thumbnail URL for a file
pub async fn get_thumbnail_url(&self, account_id: &str, file_id: i64) -> Result<String> {
let url = format!("/files/preview/{file_id}");
let response: GetThumbnailUrlResponse = self.api.get(&url, Some(account_id)).await?;
Ok(response.url)
}
/// Download file content
pub async fn download_file(&self, account_id: &str, file_id: i64) -> Result<Vec<u8>> {
let url = self.get_file_url(account_id, file_id).await?;
self.api.download_file(&url, Some(account_id)).await
}
/// Download thumbnail content
pub async fn download_thumbnail(&self, account_id: &str, file_id: i64) -> Result<Vec<u8>> {
let url = self.get_thumbnail_url(account_id, file_id).await?;
self.api.download_file(&url, Some(account_id)).await
}
// ========== Trash Methods ==========
/// Get deleted files
pub async fn get_trash(&self, account_id: &str, since_time: i64) -> Result<(Vec<File>, bool)> {
let url = format!("/trash/v2?sinceTime={since_time}");
let response: GetDiffResponse = self.api.get(&url, Some(account_id)).await?;
Ok((response.diff, response.has_more))
}
/// Permanently delete files from trash
pub async fn delete_from_trash(&self, account_id: &str, file_ids: &[i64]) -> Result<()> {
let body = serde_json::json!({
"fileIDs": file_ids
});
let _: serde_json::Value = self
.api
.post("/trash/delete", &body, Some(account_id))
.await?;
Ok(())
}
/// Empty all trash
pub async fn empty_trash(&self, account_id: &str) -> Result<()> {
self.api.delete("/trash/empty", Some(account_id)).await
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
async fn test_file_url_generation() {
let api = ApiClient::new(None).unwrap();
let methods = ApiMethods::new(&api);
// For production endpoint, should use CDN
let url = methods.get_file_url("test", 12345).await;
assert!(url.is_ok());
assert!(url.unwrap().contains("files.ente.io"));
}
}