mcvm_shared/
lib.rs

1#![warn(missing_docs)]
2
3//! This crate contains shared data for the other MCVM crates
4//!
5//! # Features:
6//!
7//! - `schema`: Enable generation of JSON schemas using the `schemars` crate
8
9/// Common addon constructs
10pub mod addon;
11/// Tools for languages and language detection
12pub mod lang;
13/// Enums for modifications to the game
14pub mod modifications;
15/// MCVM output
16pub mod output;
17/// Common package constructs
18pub mod pkg;
19/// Other utilities
20pub mod util;
21/// Tools for dealing with version patterns
22pub mod versions;
23
24use std::{fmt::Display, str::FromStr};
25
26use anyhow::anyhow;
27#[cfg(feature = "schema")]
28use schemars::JsonSchema;
29use serde::{Deserialize, Serialize};
30
31/// The Later<T> enum
32pub mod later {
33	/// An enum very similar to `Option<T>` that lets us access it with an easier assertion.
34	/// It is meant for data that we know should already be full at some point.
35	#[derive(Clone, Debug, Default, PartialEq, Eq, Hash)]
36	pub enum Later<T> {
37		/// The Later does not contain a value
38		#[default]
39		Empty,
40		/// The later does contain a value
41		Full(T),
42	}
43
44	impl<T> Later<T> {
45		/// Construct an empty Later
46		#[inline(always)]
47		pub fn new() -> Self {
48			Self::Empty
49		}
50
51		/// Fill the Later with a value
52		#[inline(always)]
53		pub fn fill(&mut self, value: T) {
54			*self = Self::Full(value);
55		}
56
57		/// Fill the Later with a function if it is not full already
58		pub fn ensure_full(&mut self, f: impl Fn() -> T) {
59			if self.is_empty() {
60				self.fill(f());
61			}
62		}
63
64		/// Clear the Later
65		#[inline(always)]
66		pub fn clear(&mut self) {
67			*self = Self::Empty;
68		}
69
70		/// Checks if the Later does not contain a value
71		#[must_use]
72		#[inline(always)]
73		pub fn is_empty(&self) -> bool {
74			matches!(self, Self::Empty)
75		}
76
77		/// Checks if the Later does contain a value
78		#[must_use]
79		#[inline(always)]
80		pub fn is_full(&self) -> bool {
81			matches!(self, Self::Full(..))
82		}
83
84		/// Grab the value inside and panic if it isn't there
85		#[inline(always)]
86		pub fn get(&self) -> &T {
87			if let Self::Full(value) = self {
88				value
89			} else {
90				self.fail();
91			}
92		}
93
94		/// Grab the value inside mutably and panic if it isn't there
95		#[inline(always)]
96		pub fn get_mut(&mut self) -> &mut T {
97			if let Self::Full(value) = self {
98				value
99			} else {
100				self.fail();
101			}
102		}
103
104		/// Grab the value inside without a reference and panic if it isn't there
105		#[inline(always)]
106		pub fn get_val(self) -> T {
107			if let Self::Full(value) = self {
108				value
109			} else {
110				self.fail();
111			}
112		}
113
114		/// Converts to an `Option<T>`
115		#[inline(always)]
116		pub fn into_option(self) -> Option<T> {
117			match self {
118				Self::Empty => None,
119				Self::Full(val) => Some(val),
120			}
121		}
122
123		#[cold]
124		fn fail(&self) -> ! {
125			panic!("Value in Later<T> does not exist");
126		}
127	}
128
129	impl<T: Clone> Later<T> {
130		/// Grab the value by cloning and panic if it isn't there
131		pub fn get_clone(&self) -> T {
132			if let Self::Full(value) = self {
133				value.clone()
134			} else {
135				self.fail();
136			}
137		}
138	}
139
140	#[cfg(test)]
141	mod tests {
142		use super::*;
143
144		#[test]
145		fn test_later_fill() {
146			let mut later = Later::new();
147			later.fill(7);
148			later.get();
149		}
150
151		#[test]
152		#[should_panic(expected = "Value in Later<T> does not exist")]
153		fn test_later_fail() {
154			let later: Later<i32> = Later::new();
155			later.get();
156		}
157	}
158}
159
160/// Minecraft game side, client or server
161#[derive(Debug, Clone, Copy, Hash, PartialEq, Eq, Deserialize, Serialize)]
162#[cfg_attr(feature = "schema", derive(JsonSchema))]
163#[serde(rename_all = "snake_case")]
164pub enum Side {
165	/// The default game
166	Client,
167	/// A dedicated server
168	Server,
169}
170
171impl Side {
172	/// Parse a Side from a string
173	pub fn parse_from_str(string: &str) -> Option<Self> {
174		match string {
175			"client" => Some(Self::Client),
176			"server" => Some(Self::Server),
177			_ => None,
178		}
179	}
180}
181
182impl FromStr for Side {
183	type Err = anyhow::Error;
184
185	fn from_str(s: &str) -> Result<Self, Self::Err> {
186		Self::parse_from_str(s).ok_or(anyhow!("Not a valid side"))
187	}
188}
189
190impl Display for Side {
191	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
192		write!(
193			f,
194			"{}",
195			match self {
196				Self::Client => "client",
197				Self::Server => "server",
198			}
199		)
200	}
201}
202
203/// Types and structs for IDs of and references to things
204pub mod id {
205	use std::sync::Arc;
206
207	/// The ID for an instance
208	pub type InstanceID = Arc<str>;
209
210	/// The ID for a profile
211	pub type ProfileID = Arc<str>;
212}