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
95
96
97
98
use quartz_nbt::NbtCompound;
use serde::{Deserialize, Serialize};

use crate::{error::CobbleError, CobbleResult};

/// Represents a single minecraft server.
/// Information is from the servers.dat file.
#[derive(Clone, Debug, Deserialize, Serialize)]
pub struct Server {
    /// Name of the server.
    pub name: String,
    /// IP address of the server.
    pub ip: String,
    /// The icon the server provides.
    /// PNG format base64 encoded.
    pub icon: Option<String>,
    /// Whether the user accepted server resource packs.
    pub accept_textures: AcceptTextures,
}

impl Server {
    /// Decode the base64 encoded string into binary PNG format.
    pub fn decode_icon(&self) -> Option<CobbleResult<Vec<u8>>> {
        if let Some(icon) = &self.icon {
            return match base64::decode(icon) {
                Ok(icon) => Some(Ok(icon)),
                Err(err) => Some(Err(err.into())),
            };
        }

        None
    }
}

/// Whether to install a server resource pack
#[derive(Clone, Debug, Deserialize, Serialize)]
pub enum AcceptTextures {
    /// User gets prompted.
    Prompt,
    /// Server resources are enabled.
    Enabled,
    /// Server resources are disabled.
    Disabled,
}

impl TryFrom<&NbtCompound> for Server {
    type Error = CobbleError;

    fn try_from(value: &NbtCompound) -> Result<Self, Self::Error> {
        let name = value.get::<_, &str>("name")?;
        let ip = value.get::<_, &str>("ip")?;

        let mut server = Server {
            name: name.to_string(),
            ip: ip.to_string(),
            icon: None,
            accept_textures: AcceptTextures::Prompt,
        };

        if value.contains_key("acceptTextures") {
            match value.get::<_, i8>("acceptTextures")? {
                0 => server.accept_textures = AcceptTextures::Disabled,
                1 => server.accept_textures = AcceptTextures::Enabled,
                _ => {}
            }
        }

        if value.contains_key("icon") {
            let icon = value.get::<_, &str>("icon")?;
            server.icon = Some(icon.to_string());
        }

        Ok(server)
    }
}

impl TryFrom<i32> for AcceptTextures {
    type Error = anyhow::Error;

    fn try_from(value: i32) -> Result<Self, Self::Error> {
        match value {
            -1 => Ok(Self::Prompt),
            0 => Ok(Self::Disabled),
            1 => Ok(Self::Enabled),
            _ => Err(anyhow::anyhow!("Invalid accept_textures value")),
        }
    }
}

impl From<AcceptTextures> for i32 {
    fn from(value: AcceptTextures) -> Self {
        match value {
            AcceptTextures::Prompt => -1,
            AcceptTextures::Disabled => 0,
            AcceptTextures::Enabled => 1,
        }
    }
}