opentalk_nextcloud_client/
share_updater.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, ShareId, SharePermission,
15};
16
17#[derive(Debug, Clone, PartialEq, Eq, Serialize)]
18#[serde(rename_all = "camelCase")]
19enum ParameterUpdate {
20 PublicUpload(bool),
21 Permissions(#[serde(with = "crate::utils::share_permissions")] HashSet<SharePermission>),
22 ExpireDate(String),
23 Note(String),
24 Label(String),
25}
26
27#[derive(Default, Debug, Clone, PartialEq, Eq, Serialize)]
28#[serde(rename_all = "camelCase")]
29struct Parameters {
30 #[serde(skip_serializing_if = "Option::is_none")]
31 public_upload: Option<bool>,
32 #[serde(skip_serializing_if = "Option::is_none")]
33 password: Option<String>,
34 #[serde(
35 with = "crate::utils::optional_share_permissions",
36 skip_serializing_if = "Option::is_none"
37 )]
38 permissions: Option<HashSet<SharePermission>>,
39 #[serde(
40 with = "crate::utils::optional_naive_date",
41 skip_serializing_if = "Option::is_none"
42 )]
43 expire_date: Option<NaiveDate>,
44 #[serde(skip_serializing_if = "Option::is_none")]
45 note: Option<String>,
46 #[serde(skip_serializing_if = "Option::is_none")]
47 label: Option<String>,
48}
49
50#[must_use]
51pub struct ShareUpdater {
52 client: Client,
53 share_id: ShareId,
54}
55
56impl ShareUpdater {
57 pub(crate) fn new(client: Client, share_id: ShareId) -> Self {
58 Self { client, share_id }
59 }
60
61 pub async fn public_upload(self, public_upload: bool) -> Result<OcsShareAnswer<OcsShareData>> {
62 self.send(ParameterUpdate::PublicUpload(public_upload))
63 .await
64 }
65
66 pub async fn permissions(
67 self,
68 permissions: HashSet<SharePermission>,
69 ) -> Result<OcsShareAnswer<OcsShareData>> {
70 self.send(ParameterUpdate::Permissions(permissions)).await
71 }
72
73 pub async fn expire_date(
74 self,
75 expire_date: Option<NaiveDate>,
76 ) -> Result<OcsShareAnswer<OcsShareData>> {
77 self.send(ParameterUpdate::ExpireDate(
78 expire_date
79 .map(|date| date.format("%Y-%m-%d").to_string())
80 .unwrap_or_default(),
81 ))
82 .await
83 }
84
85 pub async fn note<N: Into<String>>(self, note: N) -> Result<OcsShareAnswer<OcsShareData>> {
86 self.send(ParameterUpdate::Note(note.into())).await
87 }
88
89 pub async fn label<L: Into<String>>(self, label: L) -> Result<OcsShareAnswer<OcsShareData>> {
90 self.send(ParameterUpdate::Label(label.into())).await
91 }
92
93 async fn send(self, parameter: ParameterUpdate) -> Result<OcsShareAnswer<OcsShareData>> {
94 let Self { client, share_id } = self;
95
96 let url = client
97 .share_api_base_url()?
98 .join("shares/")?
99 .join(share_id.as_str())?;
100 let request = client
101 .inner
102 .http_client
103 .put(url)
104 .basic_auth(&client.inner.username, Some(&client.inner.password))
105 .json(¶meter);
106 let answer = request.send().await?;
107
108 match answer.status() {
109 StatusCode::CONTINUE | StatusCode::OK => {}
110 StatusCode::BAD_REQUEST => {
111 return Err(Error::WrongParameter);
113 }
114 StatusCode::UNAUTHORIZED => {
115 return Err(Error::Unauthorized);
117 }
118 StatusCode::FORBIDDEN => {
119 return Err(Error::PublicUploadDisabledByAdmin);
121 }
122 StatusCode::NOT_FOUND => {
123 return Err(Error::ShareNotFound { share_id });
125 }
126 status_code => {
127 warn!("Received unexpected status code {status_code} from NextCloud server.");
128 match answer.text().await {
129 Ok(text) => {
130 warn!("Response for unexpected status code {status_code}:\n{text}");
131 }
132 Err(e) => {
133 warn!("Error retrieving body from NextCloud: {}", e);
134 }
135 }
136 return Err(Error::UnexpectedStatusCode { status_code });
137 }
138 }
139 let answer: ShareAnswer<OcsShareData> = answer.json().await?;
140 Ok(answer.ocs)
141 }
142}