opentalk_nextcloud_client/
share_creator.rs1use std::collections::HashSet;
6
7use chrono::NaiveDate;
8use log::warn;
9use reqwest::StatusCode;
10use serde::Serialize;
11
12use crate::{
13 types::{OcsShareAnswer, OcsShareData, ShareAnswer},
14 Client, Error, Result, SharePermission, ShareType,
15};
16
17#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize)]
18#[serde(rename_all = "camelCase")]
19struct Parameters {
20 path: String,
21 #[serde(with = "crate::utils::share_type")]
22 share_type: ShareType,
23 #[serde(skip_serializing_if = "Option::is_none")]
24 public_upload: Option<bool>,
25 #[serde(skip_serializing_if = "Option::is_none")]
26 password: Option<String>,
27 #[serde(
28 with = "crate::utils::optional_share_permissions",
29 skip_serializing_if = "Option::is_none"
30 )]
31 permissions: Option<HashSet<SharePermission>>,
32 #[serde(
33 with = "crate::utils::optional_naive_date",
34 skip_serializing_if = "Option::is_none"
35 )]
36 expire_date: Option<NaiveDate>,
37 #[serde(skip_serializing_if = "Option::is_none")]
38 note: Option<String>,
39 #[serde(skip_serializing_if = "Option::is_none")]
40 label: Option<String>,
41}
42
43#[must_use]
44pub struct ShareCreator {
45 client: Client,
46 parameters: Parameters,
47}
48
49impl ShareCreator {
50 pub(crate) fn new(client: Client, path: String, share_type: ShareType) -> Self {
51 Self {
52 client,
53 parameters: Parameters {
54 path,
55 share_type,
56 ..Default::default()
57 },
58 }
59 }
60
61 pub fn public_upload(mut self, public_upload: bool) -> Self {
62 self.parameters.public_upload = Some(public_upload);
63 self
64 }
65
66 pub fn password<P: Into<String>>(mut self, password: P) -> Self {
67 self.parameters.password = Some(password.into());
68 self
69 }
70
71 pub fn permission(mut self, permission: SharePermission) -> Self {
72 self.parameters
73 .permissions
74 .get_or_insert(Default::default())
75 .insert(permission);
76 self
77 }
78
79 pub fn expire_date(mut self, expire_date: NaiveDate) -> Self {
80 self.parameters.expire_date = Some(expire_date);
81 self
82 }
83
84 pub fn note<N: Into<String>>(mut self, note: N) -> Self {
85 self.parameters.note = Some(note.into());
86 self
87 }
88
89 pub fn label<L: Into<String>>(mut self, label: L) -> Self {
90 self.parameters.label = Some(label.into());
91 self
92 }
93
94 pub async fn send(self) -> Result<OcsShareAnswer<OcsShareData>> {
95 let Self { client, parameters } = self;
96
97 let url = client.share_api_base_url()?.join("shares")?;
98 let request = client
99 .inner
100 .http_client
101 .post(url)
102 .basic_auth(&client.inner.username, Some(&client.inner.password))
103 .json(¶meters);
104 let answer = request.send().await?;
105
106 match answer.status() {
107 StatusCode::CONTINUE | StatusCode::OK => {}
108 StatusCode::BAD_REQUEST => {
109 return Err(Error::UnknownShareType);
111 }
112 StatusCode::UNAUTHORIZED => {
113 return Err(Error::Unauthorized);
115 }
116 StatusCode::FORBIDDEN => {
117 return Err(Error::PublicUploadDisabledByAdmin);
119 }
120 StatusCode::NOT_FOUND => {
121 return Err(Error::FileCouldNotBeShared);
123 }
124 status_code => {
125 warn!("Received unexpected status code {status_code} from NextCloud server.");
126 match answer.text().await {
127 Ok(text) => {
128 warn!("Response for unexpected status code {status_code}:\n{text}");
129 }
130 Err(e) => {
131 warn!("Error retrieving body from NextCloud: {e}");
132 }
133 }
134 return Err(Error::UnexpectedStatusCode { status_code });
135 }
136 }
137 let answer: ShareAnswer<OcsShareData> = answer.json().await?;
138 Ok(answer.ocs)
139 }
140}