pcloud/folder/
mod.rs

1use std::{borrow::Cow, cmp::Ordering};
2
3use serde::ser::SerializeStruct;
4
5pub mod create;
6pub mod delete;
7pub mod list;
8pub mod movefolder;
9pub mod rename;
10
11pub const ROOT: u64 = 0;
12
13/// Internal response structure for folder-related API calls.
14#[derive(Debug, serde::Deserialize)]
15pub(crate) struct FolderResponse {
16    /// Metadata of the folder being returned.
17    pub metadata: Folder,
18}
19
20/// Enumeration for identifying a folder by either its path or folder ID.
21#[derive(Debug)]
22pub enum FolderIdentifier<'a> {
23    /// A folder is identified by its path.
24    Path(Cow<'a, str>),
25
26    /// A folder is identified by its unique folder ID.
27    FolderId(u64),
28}
29
30impl<'a> FolderIdentifier<'a> {
31    /// Create a folder identifier using a path.
32    #[inline]
33    pub fn path<P: Into<Cow<'a, str>>>(value: P) -> Self {
34        Self::Path(value.into())
35    }
36
37    /// Create a folder identifier using a folder ID.
38    #[inline]
39    pub fn folder_id(value: u64) -> Self {
40        Self::FolderId(value)
41    }
42}
43
44impl Default for FolderIdentifier<'_> {
45    fn default() -> Self {
46        Self::FolderId(0)
47    }
48}
49
50impl<'a> From<&'a str> for FolderIdentifier<'a> {
51    fn from(value: &'a str) -> Self {
52        Self::Path(Cow::Borrowed(value))
53    }
54}
55
56impl<'a> From<Cow<'a, str>> for FolderIdentifier<'a> {
57    fn from(value: Cow<'a, str>) -> Self {
58        Self::Path(value)
59    }
60}
61
62impl<'a> From<&'a String> for FolderIdentifier<'a> {
63    fn from(value: &'a String) -> Self {
64        Self::Path(Cow::Borrowed(value.as_str()))
65    }
66}
67
68impl From<String> for FolderIdentifier<'_> {
69    fn from(value: String) -> Self {
70        Self::Path(Cow::Owned(value))
71    }
72}
73
74impl From<u64> for FolderIdentifier<'_> {
75    fn from(value: u64) -> Self {
76        Self::FolderId(value)
77    }
78}
79
80impl serde::Serialize for FolderIdentifier<'_> {
81    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
82    where
83        S: serde::Serializer,
84    {
85        let mut builder = serializer.serialize_struct(stringify!(FolderIdentifier), 1)?;
86        match self {
87            Self::FolderId(folder_id) => {
88                builder.serialize_field("folderid", folder_id)?;
89            }
90            Self::Path(path) => {
91                builder.serialize_field("path", path)?;
92            }
93        }
94        builder.end()
95    }
96}
97
98/// A structure representing a folder in pCloud.
99#[derive(Clone, Debug, serde::Deserialize, serde::Serialize)]
100pub struct Folder {
101    /// Metadata common to all entries in pCloud.
102    #[serde(flatten)]
103    pub base: crate::entry::EntryBase,
104
105    /// The unique folder ID.
106    #[serde(rename = "folderid")]
107    pub folder_id: u64,
108
109    /// A list of contents inside the folder (files and subfolders).
110    pub contents: Option<Vec<crate::entry::Entry>>,
111}
112
113impl Eq for Folder {}
114
115impl PartialEq for Folder {
116    fn eq(&self, other: &Self) -> bool {
117        self.base.id.eq(&other.base.id)
118    }
119}
120
121impl Ord for Folder {
122    fn cmp(&self, other: &Self) -> Ordering {
123        self.base.name.cmp(&other.base.name)
124    }
125}
126
127impl PartialOrd for Folder {
128    fn partial_cmp(&self, other: &Self) -> Option<Ordering> {
129        Some(self.cmp(other))
130    }
131}
132
133impl Folder {
134    /// Finds an entry (file or folder) by its name inside the folder.
135    ///
136    /// # Arguments
137    ///
138    /// * `name` - The name of the entry to search for.
139    ///
140    /// # Returns
141    ///
142    /// An optional reference to the entry if it exists in the folder.
143    ///
144    /// # Examples
145    ///
146    /// ```rust
147    /// # async fn example(folder: &pcloud::folder::Folder) {
148    /// if let Some(entry) = folder.find_entry("example.txt") {
149    ///     println!("Found entry: {:?}", entry.base().name);
150    /// }
151    /// # }
152    /// ```
153    pub fn find_entry(&self, name: &str) -> Option<&crate::entry::Entry> {
154        self.contents
155            .as_ref()
156            .and_then(|list| list.iter().find(|item| item.base().name == name))
157    }
158
159    /// Finds a file by its name inside the folder.
160    ///
161    /// # Arguments
162    ///
163    /// * `name` - The name of the file to search for.
164    ///
165    /// # Returns
166    ///
167    /// An optional reference to the file if it exists in the folder.
168    pub fn find_file(&self, name: &str) -> Option<&crate::file::File> {
169        self.contents.as_ref().and_then(|list| {
170            list.iter()
171                .filter_map(|item| item.as_file())
172                .find(|item| item.base.name == name)
173        })
174    }
175
176    /// Finds a subfolder by its name inside the folder.
177    ///
178    /// # Arguments
179    ///
180    /// * `name` - The name of the subfolder to search for.
181    ///
182    /// # Returns
183    ///
184    /// An optional reference to the subfolder if it exists in the folder.
185    pub fn find_folder(&self, name: &str) -> Option<&Folder> {
186        self.contents.as_ref().and_then(|list| {
187            list.iter()
188                .filter_map(|item| item.as_folder())
189                .find(|item| item.base.name == name)
190        })
191    }
192}
193
194/// Internal wrapper for a folder identifier used in API requests.
195pub(crate) struct ToFolderIdentifier<'a>(pub FolderIdentifier<'a>);
196
197impl serde::Serialize for ToFolderIdentifier<'_> {
198    fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
199    where
200        S: serde::Serializer,
201    {
202        let mut builder = serializer.serialize_struct(stringify!(FolderIdentifier), 1)?;
203        match self.0 {
204            FolderIdentifier::FolderId(ref folder_id) => {
205                builder.serialize_field("tofolderid", folder_id)?;
206            }
207            FolderIdentifier::Path(ref path) => {
208                builder.serialize_field("topath", path)?;
209            }
210        }
211        builder.end()
212    }
213}