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
99
100
101
102
103
104
105
106
107
use std::fmt::Display;

use serde::Deserialize;

use crate::pkg::{PackageAddonOptionalHashes, PkgIdentifier};

/// Different kinds of addons
#[derive(Debug, Clone, Copy, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum AddonKind {
	/// A Minecraft resource pack
	ResourcePack,
	/// A game modification that needs to be loaded by a custom loader
	Mod,
	/// A server plugin that modifies game behavior
	Plugin,
	/// A graphics shader that needs to be loaded by a shader modification
	Shader,
	/// A Minecraft datapack
	Datapack,
}

impl AddonKind {
	/// Parse an AddonKind from a string
	pub fn parse_from_str(string: &str) -> Option<Self> {
		match string {
			"resource_pack" => Some(Self::ResourcePack),
			"mod" => Some(Self::Mod),
			"plugin" => Some(Self::Plugin),
			"shader" => Some(Self::Shader),
			"datapack" => Some(Self::Datapack),
			_ => None,
		}
	}

	/// Plural version of to_string
	pub fn to_plural_string(&self) -> String {
		match self {
			Self::ResourcePack => String::from("resource_packs"),
			Self::Mod => String::from("mods"),
			Self::Plugin => String::from("plugins"),
			Self::Shader => String::from("shaders"),
			Self::Datapack => String::from("datapacks"),
		}
	}

	/// Gets the file extension for this addon kind
	pub fn get_extension(&self) -> &str {
		match self {
			AddonKind::Mod | AddonKind::Plugin => ".jar",
			AddonKind::ResourcePack | AddonKind::Shader | AddonKind::Datapack => ".zip",
		}
	}
}

impl Display for AddonKind {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(
			f,
			"{}",
			match self {
				Self::ResourcePack => "resource_pack",
				Self::Mod => "mod",
				Self::Plugin => "plugin",
				Self::Shader => "shader",
				Self::Datapack => "datapack",
			}
		)
	}
}

/// Some content that is installed on Minecraft
#[derive(Debug, Clone)]
pub struct Addon {
	/// What type of addon this is
	pub kind: AddonKind,
	/// The ID of this addon, unique among a package
	pub id: String,
	/// The addon's file name
	pub file_name: String,
	/// The ID of the package that installed this addon
	pub pkg_id: PkgIdentifier,
	/// Version of the addon, used for caching
	pub version: Option<String>,
	/// Hashes of the addon
	pub hashes: PackageAddonOptionalHashes,
}

/// Checks for a valid addon version identifier that is compatible with all systems
pub fn is_addon_version_valid(version: &str) -> bool {
	if !version.is_ascii() {
		return false;
	}

	for c in version.chars() {
		if !c.is_ascii_alphanumeric() && c != '-' {
			return false;
		}
	}

	true
}

/// Checks for a valid addon filename
pub fn is_filename_valid(kind: AddonKind, filename: &str) -> bool {
	filename.ends_with(kind.get_extension())
}