Skip to main content

nextcloud_passwords_client/
service.rs

1use crate::{AuthenticatedApi, Error};
2use serde::{Deserialize, Serialize};
3
4/// Access the service API
5pub struct ServiceApi<'a> {
6    pub(crate) api: &'a AuthenticatedApi,
7}
8
9impl<'a> ServiceApi<'a> {
10    /// Generates a password with the users default settings
11    pub async fn generate_password_with_user_settings(&self) -> Result<GenerateResponse, Error> {
12        self.api.passwords_get("1.0/service/password", ()).await
13    }
14    /// The password action generates one password with the given settings.
15    ///
16    /// Notes
17    ///  - Generated passwords are checked for security automatically
18    ///  - The maximum value for strength is 4
19    pub async fn generate_password(
20        &self,
21        settings: GeneratePassword,
22    ) -> Result<GenerateResponse, Error> {
23        self.api
24            .passwords_post("1.0/service/password", settings)
25            .await
26    }
27
28    /// The avatar action returns a png avatar icon for the given user id.
29    ///
30    /// Notes
31    ///  - If the user did not specify an avatar a default image will be generated
32    pub async fn avatar(
33        &self,
34        user: uuid::Uuid,
35        MiniatureSize(size): MiniatureSize,
36    ) -> Result<bytes::Bytes, Error> {
37        self.api
38            .bytes_request(
39                format!("1.0/service/avatar/{user}/{size}", user = user, size = size),
40                reqwest::Method::GET,
41                (),
42            )
43            .await
44    }
45
46    /// The favicon action returns a png favicon icon for the given domain.
47    ///
48    /// Notes
49    ///  - If no favicon can be found a default image will be generated
50    pub async fn favicon(
51        &self,
52        domain: String,
53        MiniatureSize(size): MiniatureSize,
54    ) -> Result<bytes::Bytes, Error> {
55        self.api
56            .bytes_request(
57                format!(
58                    "1.0/service/favicon/{domain}/{size}",
59                    domain = domain,
60                    size = size
61                ),
62                reqwest::Method::GET,
63                (),
64            )
65            .await
66    }
67    /// The preview action returns a jpeg preview image for the given domain.
68    ///
69    /// The default width is 640
70    /// The default height is 360...
71    /// The default view is Desktop
72    ///
73    /// Notes
74    ///  - If no image can be created a default image will be used
75    ///  - This action is known to be slow if the cache is empty
76    ///  - The width and height must be a multiple of 10
77    ///  - The minimum width is 240 pixels
78    ///  - The maximum width is 1280 pixels
79    ///  - The minimum height is 240 pixels
80    ///  - The maximum height is 1280 pixels
81    ///  - If a width and height were specified, the image will be cropped to fill the area
82    ///  - The width and height can be 0. In this case, it is up to the api to set the optimal value
83    ///  - You can specify a range for width and height by passing SIZE..., SIZE...SIZE or ...SIZE where size is a number.
84    ///  The left value will be the minimum and the right value the maximum.
85    ///  The api will try to generate an image that fits the given values without cropping
86    pub async fn preview(
87        &self,
88        domain: String,
89        view: Option<View>,
90        width: Option<String>,
91        height: Option<String>,
92    ) -> Result<bytes::Bytes, Error> {
93        let view = view.unwrap_or(View::Desktop).as_str();
94        let width = width.unwrap_or("640".into());
95        let height = height.unwrap_or("360...".into());
96
97        self.api
98            .bytes_request(
99                format!(
100                    "1.0/service/preview/{domain}/{view}/{width}/{height}",
101                    domain = domain,
102                    view = view,
103                    width = width,
104                    height = height,
105                ),
106                reqwest::Method::GET,
107                (),
108            )
109            .await
110    }
111}
112
113#[derive(Debug)]
114pub enum View {
115    Desktop,
116    Mobile,
117}
118impl View {
119    fn as_str(&self) -> &'static str {
120        match self {
121            Self::Desktop => "desktop",
122            Self::Mobile => "mobile",
123        }
124    }
125}
126
127/// Represent the possible sizes of an avatar
128#[derive(Debug)]
129pub struct MiniatureSize(u16);
130impl MiniatureSize {
131    /// The size must be a multiple of 8, The minimum size is 16 pixels, The maximum size is 256 pixels
132    pub fn new(size: u16) -> Option<Self> {
133        if size % 8 != 0 || size < 16 || size > 256 {
134            None
135        } else {
136            Some(Self(size))
137        }
138    }
139}
140impl Default for MiniatureSize {
141    fn default() -> Self {
142        MiniatureSize(32)
143    }
144}
145
146#[derive(Debug, Serialize)]
147#[serde(transparent)]
148pub struct PasswordStrength(u8);
149impl PasswordStrength {
150    #[inline]
151    pub fn one() -> Self {
152        PasswordStrength(1)
153    }
154    #[inline]
155    pub fn two() -> Self {
156        PasswordStrength(2)
157    }
158    #[inline]
159    pub fn three() -> Self {
160        PasswordStrength(3)
161    }
162    #[inline]
163    pub fn four() -> Self {
164        PasswordStrength(4)
165    }
166    #[inline]
167    pub fn value(&self) -> u8 {
168        self.0
169    }
170}
171
172#[derive(Serialize, Default, Debug)]
173pub struct GeneratePassword {
174    #[serde(skip_serializing_if = "Option::is_none")]
175    strength: Option<PasswordStrength>,
176    #[serde(skip_serializing_if = "Option::is_none")]
177    numbers: Option<bool>,
178    #[serde(skip_serializing_if = "Option::is_none")]
179    special: Option<bool>,
180}
181impl GeneratePassword {
182    pub fn new() -> Self {
183        Default::default()
184    }
185
186    /// A higher value creates a longer and more complex password (Default = 1)
187    pub fn strength(self, strength: PasswordStrength) -> Self {
188        Self {
189            strength: Some(strength),
190            ..self
191        }
192    }
193    /// Whether or not numbers should be used in the password (Default = false)
194    pub fn numbers(self, numbers: bool) -> Self {
195        Self {
196            numbers: Some(numbers),
197            ..self
198        }
199    }
200    /// Whether or not special characters should be used in the password
201    pub fn special(self, special: bool) -> Self {
202        Self {
203            special: Some(special),
204            ..self
205        }
206    }
207}
208
209#[derive(Serialize, Deserialize, Debug)]
210pub struct GenerateResponse {
211    pub password: String,
212    pub words: Vec<String>,
213    pub strength: u8,
214    pub numbers: bool,
215    pub special: bool,
216}