cubiomes_sys/
enums.rs

1//! This module contains generated rust enums representing different cubiomes
2//! enums. It also implements display and fromstr for them.
3//!
4//! The enums in this module are automatically generated from the version of
5//! cubiomes this crate links against. It also adds methods to display the
6//! structures.
7//!
8//! The display methods will panic if cubiomes generates invalid data. Cubiomes
9//! should never generate invalid data, so the functions should never panic
10use core::str;
11use std::{
12    error::Error,
13    ffi::{CStr, CString},
14    fmt::Display,
15    str::FromStr,
16};
17
18use num_traits::{FromPrimitive, ToPrimitive};
19
20use crate::{biome2str, mc2str, str2mc, struct2str};
21
22#[derive(Debug, Clone, PartialEq, Eq)]
23pub enum ParseError {
24    NonAsciiStr,
25    NotMCVersion,
26    NotDimension,
27    InvalidCString(std::ffi::NulError),
28}
29
30impl Display for ParseError {
31    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
32        match self {
33            ParseError::NonAsciiStr => write!(f, "Value is not an ascii sequence"),
34            ParseError::NotMCVersion => write!(f, "Value does not represent a minecraft version"),
35            ParseError::NotDimension => {
36                write!(f, "Value does not represent a valid minecraft dimension")
37            }
38            ParseError::InvalidCString(_) => {
39                write!(f, "Could not format self as a c string")
40            }
41        }
42    }
43}
44
45impl Error for ParseError {
46    fn source(&self) -> Option<&(dyn Error + 'static)> {
47        if let Self::InvalidCString(e) = self {
48            Some(e)
49        } else {
50            None
51        }
52    }
53
54    fn cause(&self) -> Option<&dyn Error> {
55        self.source()
56    }
57}
58
59impl From<std::ffi::NulError> for ParseError {
60    fn from(value: std::ffi::NulError) -> Self {
61        Self::InvalidCString(value)
62    }
63}
64
65include!(concat!(env!("OUT_DIR"), "/biome_enums.rs"));
66
67impl BiomeID {
68    /// Converts the enum to its string representation in the specified version.
69    ///
70    /// We can't implement display or fromstr for this, since some biomeids just
71    /// go renamed in 1.18, and as such, formatting differs per minecraft
72    /// version.
73    pub fn to_mc_biome_str(&self, version: MCVersion) -> &'static str {
74        let chars = unsafe { biome2str(version as i32, self.to_i32().unwrap()) };
75
76        // Assert that chars is not null, note that it should never be, but the api
77        // could theoretically return null if somehow the MCVersion was invalid.
78        assert!(!chars.is_null());
79
80        let slice = unsafe { CStr::from_ptr(chars) };
81
82        match slice.to_str() {
83            Ok(str) => str,
84            Err(_) => panic!(),
85        }
86    }
87}
88
89/// Formats the structure as its name
90impl Display for StructureType {
91    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
92        let chars = unsafe { struct2str(*self as i32) };
93
94        assert!(!chars.is_null());
95        let slice = unsafe { CStr::from_ptr(chars) };
96
97        write!(f, "{}", slice.to_str().unwrap())
98    }
99}
100
101impl Display for MCVersion {
102    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
103        let chars = unsafe { mc2str(*self as i32) };
104
105        assert!(!chars.is_null());
106        let slice = unsafe { CStr::from_ptr(chars) };
107
108        write!(f, "{}", slice.to_str().unwrap())
109    }
110}
111
112impl FromStr for MCVersion {
113    type Err = ParseError;
114
115    fn from_str(s: &str) -> Result<Self, Self::Err> {
116        if !s.is_ascii() {
117            return Err(ParseError::NonAsciiStr);
118        }
119
120        let s: CString = CString::from_str(s)?;
121
122        let version = unsafe { str2mc(s.as_ptr()) };
123
124        if version == 0 {
125            Err(ParseError::NotMCVersion)
126        } else {
127            Ok(MCVersion::from_i32(version).unwrap())
128        }
129    }
130}
131
132impl Display for Dimension {
133    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
134        match self {
135            Dimension::DIM_NETHER => write!(f, "the_nether"),
136            Dimension::DIM_OVERWORLD => write!(f, "overworld"),
137            Dimension::DIM_END => write!(f, "the_end"),
138            // This should never exist unless magic was done
139            Dimension::DIM_UNDEF => write!(f, "undefined"),
140        }
141    }
142}
143
144impl FromStr for Dimension {
145    type Err = ParseError;
146
147    fn from_str(s: &str) -> Result<Self, Self::Err> {
148        match s {
149            "the_nether" => Ok(Self::DIM_NETHER),
150            "overworld" => Ok(Self::DIM_OVERWORLD),
151            "the_end" => Ok(Self::DIM_END),
152            "undefined" => Ok(Self::DIM_UNDEF),
153            _ => Err(ParseError::NotDimension),
154        }
155    }
156}
157
158#[cfg(test)]
159mod test {
160    use crate::enums::{BiomeID, Dimension, MCVersion, StructureType};
161
162    #[test]
163    fn test_biome_conversion() {
164        assert_eq!(
165            BiomeID::badlands.to_mc_biome_str(crate::enums::MCVersion::MC_1_6_4),
166            "badlands"
167        );
168
169        assert_eq!(
170            BiomeID::stony_shore.to_mc_biome_str(crate::enums::MCVersion::MC_1_18),
171            "stony_shore"
172        );
173
174        assert_eq!(
175            BiomeID::stone_shore.to_mc_biome_str(crate::enums::MCVersion::MC_1_6_4),
176            "stone_shore"
177        );
178    }
179
180    #[test]
181    fn test_structure_conversion() {
182        assert_eq!(StructureType::Bastion.to_string(), "bastion_remnant");
183        assert_eq!(StructureType::Shipwreck.to_string(), "shipwreck");
184        assert_eq!(StructureType::End_City.to_string(), "end_city");
185    }
186
187    #[test]
188    fn test_mc_version_conversion() {
189        assert_eq!(MCVersion::MC_1_15.to_string(), "1.15");
190    }
191
192    #[test]
193    fn test_mc_version_parsing() {
194        assert_eq!(
195            MCVersion::MC_B1_7,
196            "Beta 1.7".parse().expect("parsing erro")
197        );
198        assert_eq!(
199            MCVersion::MC_1_15_2,
200            "1.15.2".parse().expect("parsing erro")
201        );
202        assert_eq!(
203            MCVersion::MC_1_10_2,
204            "1.10.2".parse().expect("parsing erro")
205        );
206    }
207
208    #[test]
209    fn test_dimension_parsing() {
210        assert_eq!(
211            Dimension::DIM_END,
212            Dimension::DIM_END
213                .to_string()
214                .parse()
215                .expect("Failed to parse"),
216            "the_end"
217        );
218        assert_eq!(
219            Dimension::DIM_OVERWORLD,
220            Dimension::DIM_OVERWORLD
221                .to_string()
222                .parse()
223                .expect("Failed to parse"),
224            "overworld"
225        );
226
227        assert_eq!(
228            Dimension::DIM_NETHER,
229            Dimension::DIM_NETHER
230                .to_string()
231                .parse()
232                .expect("Failed to parse"),
233            "the_nether"
234        );
235        assert_eq!(
236            Dimension::DIM_UNDEF,
237            Dimension::DIM_UNDEF
238                .to_string()
239                .parse()
240                .expect("Failed to parse"),
241            "undefined"
242        );
243    }
244}