1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
use serde::{Deserialize, Deserializer};
use url::Url;

/// In the case of [`Category::Neko`], the API
/// also returns the source url, the name and a
/// link to the artist that made it.
#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct ImageDetails {
    #[serde(deserialize_with = "deserialize_url")]
    pub artist_href: Url,
    pub artist_name: String,
    #[serde(deserialize_with = "deserialize_url")]
    pub source_url: Url,
}

fn deserialize_url<'de, D: Deserializer<'de>>(de: D) -> Result<Url, D::Error> {
    let s = String::deserialize(de)?;
    Url::parse(&s).map_err(serde::de::Error::custom)
}

/// In the case of gif endpoints, the API also
/// returns the anime name.
#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
pub struct GifDetails {
    pub anime_name: String,
}

#[derive(Deserialize, Debug, Clone, Hash, PartialEq, Eq, PartialOrd, Ord)]
#[serde(untagged)]
#[non_exhaustive]
pub enum Details {
    Image(ImageDetails),
    Gif(GifDetails),
}

impl Details {
    /// Returns `true` if the details is [`Image`].
    ///
    /// [`Image`]: Details::Image
    pub fn is_image(&self) -> bool {
        matches!(self, Self::Image(..))
    }

    pub fn as_image(&self) -> Option<&ImageDetails> {
        if let Self::Image(v) = self {
            Some(v)
        } else {
            None
        }
    }

    pub fn try_into_image(self) -> Result<ImageDetails, Self> {
        if let Self::Image(v) = self {
            Ok(v)
        } else {
            Err(self)
        }
    }

    /// Returns `true` if the details is [`Gif`].
    ///
    /// [`Gif`]: Details::Gif
    pub fn is_gif(&self) -> bool {
        matches!(self, Self::Gif(..))
    }

    pub fn as_gif(&self) -> Option<&GifDetails> {
        if let Self::Gif(v) = self {
            Some(v)
        } else {
            None
        }
    }

    pub fn try_into_gif(self) -> Result<GifDetails, Self> {
        if let Self::Gif(v) = self {
            Ok(v)
        } else {
            Err(self)
        }
    }
}

impl From<ImageDetails> for Details {
    fn from(v: ImageDetails) -> Self {
        Self::Image(v)
    }
}

impl From<GifDetails> for Details {
    fn from(v: GifDetails) -> Self {
        Self::Gif(v)
    }
}