Skip to main content

opentalk_nextcloud_client/
share_creator.rs

1// SPDX-FileCopyrightText: OpenTalk GmbH <mail@opentalk.eu>
2//
3// SPDX-License-Identifier: EUPL-1.2
4
5use 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(&parameters);
104        let answer = request.send().await?;
105
106        match answer.status() {
107            StatusCode::CONTINUE | StatusCode::OK => {}
108            StatusCode::BAD_REQUEST => {
109                // 400
110                return Err(Error::UnknownShareType);
111            }
112            StatusCode::UNAUTHORIZED => {
113                // 401
114                return Err(Error::Unauthorized);
115            }
116            StatusCode::FORBIDDEN => {
117                // 403
118                return Err(Error::PublicUploadDisabledByAdmin);
119            }
120            StatusCode::NOT_FOUND => {
121                // 404
122                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}