pcloud/folder/create.rs
1use std::borrow::Cow;
2
3use super::{Folder, FolderIdentifier, FolderResponse};
4
5/// Parameters used for folder creation requests.
6///
7/// This struct is used internally to send a `parent` folder and `name`
8/// to the `createfolder` or `createfolderifnotexists` API endpoints.
9#[derive(Debug, serde::Serialize)]
10struct Params<'a> {
11 /// The parent folder in which the new folder will be created.
12 #[serde(flatten)]
13 parent: FolderIdentifier<'a>,
14
15 /// The name of the new folder.
16 name: Cow<'a, str>,
17}
18
19impl crate::Client {
20 /// Creates a new folder inside the specified parent folder on pCloud.
21 ///
22 /// This function calls the `createfolder` API endpoint and will return
23 /// an error if a folder with the same name already exists in the target location.
24 ///
25 /// # Arguments
26 ///
27 /// * `parent` - A value convertible into a [`FolderIdentifier`] representing the parent folder.
28 /// * `name` - The name of the folder to create.
29 ///
30 /// # Returns
31 ///
32 /// On success, returns a [`Folder`] representing the newly created folder.
33 ///
34 /// # Errors
35 ///
36 /// Returns a [`crate::Error`] if the folder already exists or the request fails.
37 ///
38 /// # Examples
39 ///
40 /// ```rust,no_run
41 /// # async fn example(client: &pcloud::Client) -> Result<(), pcloud::Error> {
42 /// let folder = client.create_folder(0, "new-folder").await?;
43 /// println!("Created folder: {}", folder.base.name);
44 /// # Ok(())
45 /// # }
46 /// ```
47 pub async fn create_folder<'a>(
48 &self,
49 parent: impl Into<FolderIdentifier<'a>>,
50 name: impl Into<Cow<'a, str>>,
51 ) -> crate::Result<Folder> {
52 let params = Params {
53 parent: parent.into(),
54 name: name.into(),
55 };
56 self.get_request::<FolderResponse, _>("createfolder", params)
57 .await
58 .map(|res| res.metadata)
59 }
60}
61
62impl crate::Client {
63 /// Creates a new folder if it does not already exist in the specified location.
64 ///
65 /// This function calls the `createfolderifnotexists` API endpoint, which ensures
66 /// the operation is idempotent: if a folder with the given name exists, it will be returned.
67 /// Otherwise, a new folder is created.
68 ///
69 /// # Arguments
70 ///
71 /// * `parent` - A value convertible into a [`FolderIdentifier`] representing the parent folder.
72 /// * `name` - The name of the folder to create or return if it exists.
73 ///
74 /// # Returns
75 ///
76 /// A [`Folder`] representing the existing or newly created folder.
77 ///
78 /// # Errors
79 ///
80 /// Returns a [`crate::Error`] if the request fails for any reason other than the folder already existing.
81 ///
82 /// # Examples
83 ///
84 /// ```rust,no_run
85 /// # async fn example(client: &pcloud::Client) -> Result<(), pcloud::Error> {
86 /// let folder = client.create_folder_if_not_exists(0, "my-folder").await?;
87 /// println!("Folder ID: {}", folder.folder_id);
88 /// # Ok(())
89 /// # }
90 /// ```
91 pub async fn create_folder_if_not_exists<'a>(
92 &self,
93 parent: impl Into<FolderIdentifier<'a>>,
94 name: impl Into<Cow<'a, str>>,
95 ) -> crate::Result<Folder> {
96 let params = Params {
97 parent: parent.into(),
98 name: name.into(),
99 };
100 self.get_request::<FolderResponse, _>("createfolderifnotexists", params)
101 .await
102 .map(|res| res.metadata)
103 }
104}
105
106#[cfg(test)]
107mod tests {
108 use crate::{Client, Credentials};
109 use mockito::Matcher;
110
111 #[tokio::test]
112 async fn success() {
113 let mut server = mockito::Server::new_async().await;
114 let m = server
115 .mock("GET", "/createfolder")
116 .match_query(Matcher::AllOf(vec![
117 Matcher::UrlEncoded("access_token".into(), "access-token".into()),
118 Matcher::UrlEncoded("folderid".into(), "0".into()),
119 Matcher::UrlEncoded("name".into(), "testing".into()),
120 ]))
121 .with_status(200)
122 .with_body(
123 r#"{
124 "result": 0,
125 "metadata": {
126 "path": "\/testing",
127 "name": "testing",
128 "created": "Fri, 23 Jul 2021 19:39:09 +0000",
129 "ismine": true,
130 "thumb": false,
131 "modified": "Fri, 23 Jul 2021 19:39:09 +0000",
132 "id": "d10",
133 "isshared": false,
134 "icon": "folder",
135 "isfolder": true,
136 "parentfolderid": 0,
137 "folderid": 10
138 }
139}"#,
140 )
141 .create();
142 let client = Client::new(server.url(), Credentials::access_token("access-token")).unwrap();
143 let result = client.create_folder(0, "testing").await.unwrap();
144 assert_eq!(result.base.name, "testing");
145 m.assert();
146 }
147
148 #[tokio::test]
149 async fn error() {
150 let mut server = mockito::Server::new_async().await;
151 let m = server
152 .mock("GET", "/createfolder")
153 .match_query(Matcher::AllOf(vec![
154 Matcher::UrlEncoded("access_token".into(), "access-token".into()),
155 Matcher::UrlEncoded("folderid".into(), "0".into()),
156 Matcher::UrlEncoded("name".into(), "testing".into()),
157 ]))
158 .with_status(200)
159 .with_body(r#"{ "result": 1020, "error": "something went wrong" }"#)
160 .create();
161 let client = Client::new(server.url(), Credentials::access_token("access-token")).unwrap();
162 let error = client.create_folder(0, "testing").await.unwrap_err();
163 assert!(matches!(error, crate::Error::Protocol(_, _)));
164 m.assert();
165 }
166}