use serde::{Deserialize, Serialize};
use std::collections::HashMap;
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
#[serde(rename_all = "lowercase")]
pub enum EntryType {
File,
Directory,
}
impl EntryType {
pub fn is_file(&self) -> bool {
matches!(self, EntryType::File)
}
pub fn is_directory(&self) -> bool {
matches!(self, EntryType::Directory)
}
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileEntity {
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub display_name: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub file_type: Option<EntryType>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_at: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mtime: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub provided_mtime: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub crc32: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub md5: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sha1: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub sha256: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mime_type: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub region: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub permissions: Option<String>,
#[serde(rename = "subfolders_locked?", skip_serializing_if = "Option::is_none")]
pub subfolders_locked: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub is_locked: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub download_uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority_color: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preview_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub preview: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub custom_metadata: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_api_key_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_automation_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_bundle_registration_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_inbox_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_remote_server_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_remote_server_sync_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub created_by_as2_incoming_message_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_modified_by_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_modified_by_api_key_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_modified_by_automation_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_modified_by_bundle_registration_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_modified_by_remote_server_id: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub last_modified_by_remote_server_sync_id: Option<i64>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct FileUploadPartEntity {
#[serde(skip_serializing_if = "Option::is_none")]
pub upload_uri: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub http_method: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub headers: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parameters: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub part_number: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub partsize: Option<i64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub next_partsize: Option<i64>,
#[serde(rename = "ref", skip_serializing_if = "Option::is_none")]
pub ref_: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub action: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parallel_parts: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub retry_parts: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub available_parts: Option<i32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub expires: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub send: Option<HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub path: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub ask_about_overwrites: Option<bool>,
}
pub type FolderEntity = FileEntity;
#[derive(Debug, Clone, Default)]
pub struct PaginationInfo {
pub cursor_next: Option<String>,
pub cursor_prev: Option<String>,
}
impl PaginationInfo {
pub fn from_headers(headers: &reqwest::header::HeaderMap) -> Self {
let cursor_next = headers
.get("X-Files-Cursor-Next")
.and_then(|v| v.to_str().ok())
.map(String::from);
let cursor_prev = headers
.get("X-Files-Cursor-Prev")
.and_then(|v| v.to_str().ok())
.map(String::from);
Self {
cursor_next,
cursor_prev,
}
}
pub fn has_next(&self) -> bool {
self.cursor_next.is_some()
}
pub fn has_prev(&self) -> bool {
self.cursor_prev.is_some()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_file_entity_deserialize() {
let json = r#"{
"path": "/test/file.txt",
"display_name": "file.txt",
"type": "file",
"size": 1024
}"#;
let entity: FileEntity = serde_json::from_str(json).unwrap();
assert_eq!(entity.path, Some("/test/file.txt".to_string()));
assert_eq!(entity.display_name, Some("file.txt".to_string()));
assert_eq!(entity.file_type, Some(EntryType::File));
assert_eq!(entity.size, Some(1024));
}
#[test]
fn test_pagination_info_empty() {
let headers = reqwest::header::HeaderMap::new();
let info = PaginationInfo::from_headers(&headers);
assert!(!info.has_next());
assert!(!info.has_prev());
}
#[test]
fn test_pagination_info_with_next() {
let mut headers = reqwest::header::HeaderMap::new();
headers.insert("X-Files-Cursor-Next", "next-cursor".parse().unwrap());
let info = PaginationInfo::from_headers(&headers);
assert!(info.has_next());
assert!(!info.has_prev());
assert_eq!(info.cursor_next, Some("next-cursor".to_string()));
}
}