Skip to main content

cyberdrop_client/
files.rs

1use serde::Deserialize;
2use serde::de;
3
4/// File metadata as returned by the album listing endpoint.
5#[derive(Debug, Clone, PartialEq, Eq, Deserialize)]
6pub struct AlbumFile {
7    /// File numeric ID.
8    pub id: u64,
9    /// Display name.
10    pub name: String,
11    /// Uploader user ID as returned by the service.
12    #[serde(rename = "userid", deserialize_with = "de_string_or_number")]
13    pub user_id: String,
14    /// File size in bytes.
15    #[serde(deserialize_with = "de_u64_or_string")]
16    pub size: u64,
17    /// Service-provided timestamp value.
18    pub timestamp: u64,
19    /// Last visited timestamp, if returned by the service.
20    #[serde(rename = "last_visited_at")]
21    pub last_visited_at: Option<String>,
22    /// Service-provided file slug.
23    pub slug: String,
24    /// Base domain for file media (for example, `https://sun-i.cyberdrop.cr`).
25    pub image: String,
26    /// Nullable expiry date as returned by the service.
27    pub expirydate: Option<String>,
28    /// Album ID as returned by the service.
29    #[serde(rename = "albumid", deserialize_with = "de_string_or_number")]
30    pub album_id: String,
31    /// File extension name as returned by the service.
32    pub extname: String,
33    /// Thumbnail path relative to `image` (for example, `thumbs/<...>.png`).
34    #[serde(default)]
35    pub thumb: String,
36}
37
38fn de_string_or_number<'de, D>(deserializer: D) -> Result<String, D::Error>
39where
40    D: serde::Deserializer<'de>,
41{
42    Ok(match StringOrNumber::deserialize(deserializer)? {
43        StringOrNumber::String(v) => v,
44        StringOrNumber::U64(v) => v.to_string(),
45        StringOrNumber::I64(v) => v.to_string(),
46    })
47}
48
49fn de_u64_or_string<'de, D>(deserializer: D) -> Result<u64, D::Error>
50where
51    D: serde::Deserializer<'de>,
52{
53    match StringOrNumber::deserialize(deserializer)? {
54        StringOrNumber::String(v) => v.parse::<u64>().map_err(de::Error::custom),
55        StringOrNumber::U64(v) => Ok(v),
56        StringOrNumber::I64(v) => {
57            u64::try_from(v).map_err(|_| de::Error::custom("negative value not allowed"))
58        }
59    }
60}
61
62#[derive(Deserialize)]
63#[serde(untagged)]
64enum StringOrNumber {
65    String(String),
66    U64(u64),
67    I64(i64),
68}