1use std::{any::type_name, collections::HashMap, ffi::CStr, sync::LazyLock};
2
3use fastnoise2_sys::*;
4
5use crate::{FastNoiseError, Node};
6
7#[derive(Debug)]
8pub(crate) struct Metadata {
9 #[allow(dead_code)]
10 pub id: i32,
11 #[allow(dead_code)]
12 pub name: String,
13 pub members: HashMap<String, Member>,
14}
15
16#[derive(Debug, Clone)]
17pub struct Member {
18 pub name: String,
19 pub member_type: MemberType,
20 pub index: i32,
21 pub enum_names: HashMap<String, i32>,
22}
23
24#[derive(Clone, Copy, Debug)]
26pub enum MemberType {
27 Float,
29 Int,
31 Enum,
33 NodeLookup,
35 Hybrid,
37}
38
39impl std::fmt::Display for MemberType {
40 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
41 match self {
42 Self::Float => f.write_str(type_name::<f32>()),
43 Self::Int => f.write_str(type_name::<i32>()),
44 Self::Enum => f.write_str(type_name::<&str>()),
45 Self::NodeLookup => f.write_str(type_name::<&Node>()),
46 Self::Hybrid => f.write_fmt(format_args!("{} or {}", Self::Float, Self::NodeLookup)),
47 }
48 }
49}
50
51pub(crate) static METADATA_NAME_LOOKUP: LazyLock<HashMap<String, i32>> = LazyLock::new(|| {
52 let metadata_count = unsafe { fnGetMetadataCount() };
53 let mut lookup = HashMap::new();
54
55 for id in 0..metadata_count {
56 let name =
57 format_lookup(&unsafe { CStr::from_ptr(fnGetMetadataName(id)) }.to_string_lossy());
58 lookup.insert(name, id);
59 }
60 lookup
61});
62
63pub(crate) static NODE_METADATA: LazyLock<Vec<Metadata>> = LazyLock::new(|| {
64 let metadata_count = unsafe { fnGetMetadataCount() };
65 let mut metadata_vec = Vec::with_capacity(metadata_count as usize);
66 for id in 0..metadata_count {
67 let name =
68 format_lookup(&unsafe { CStr::from_ptr(fnGetMetadataName(id)) }.to_string_lossy());
69 let mut members = HashMap::new();
70
71 let variable_count = unsafe { fnGetMetadataVariableCount(id) };
72 let node_lookup_count = unsafe { fnGetMetadataNodeLookupCount(id) };
73 let hybrid_count = unsafe { fnGetMetadataHybridCount(id) };
74
75 for variable_idx in 0..variable_count {
76 let member_type = match unsafe { fnGetMetadataVariableType(id, variable_idx) } {
77 0 => MemberType::Float,
78 1 => MemberType::Int,
79 2 => MemberType::Enum,
80 _ => MemberType::Hybrid,
81 };
82 let dimension_idx = unsafe { fnGetMetadataVariableDimensionIdx(id, variable_idx) };
83 let name = format_dimension_member(
84 &format_lookup(
85 &unsafe { CStr::from_ptr(fnGetMetadataVariableName(id, variable_idx)) }
86 .to_string_lossy(),
87 ),
88 dimension_idx,
89 );
90 let mut enum_names = HashMap::new();
91 if let MemberType::Enum = member_type {
92 let enum_count = unsafe { fnGetMetadataEnumCount(id, variable_idx) };
93 for enum_idx in 0..enum_count {
94 let enum_name = format_lookup(
95 &unsafe {
96 CStr::from_ptr(fnGetMetadataEnumName(id, variable_idx, enum_idx))
97 }
98 .to_string_lossy(),
99 );
100 enum_names.insert(enum_name, enum_idx);
101 }
102 }
103 members.insert(
104 name.clone(),
105 Member {
106 name,
107 member_type,
108 index: variable_idx,
109 enum_names,
110 },
111 );
112 }
113
114 for node_lookup_idx in 0..node_lookup_count {
115 let dimension_idx = unsafe { fnGetMetadataNodeLookupDimensionIdx(id, node_lookup_idx) };
116 let name = format_dimension_member(
117 &format_lookup(
118 &unsafe { CStr::from_ptr(fnGetMetadataNodeLookupName(id, node_lookup_idx)) }
119 .to_string_lossy(),
120 ),
121 dimension_idx,
122 );
123 members.insert(
124 name.clone(),
125 Member {
126 name,
127 member_type: MemberType::NodeLookup,
128 index: node_lookup_idx,
129 enum_names: HashMap::new(),
130 },
131 );
132 }
133
134 for hybrid_idx in 0..hybrid_count {
135 let dimension_idx = unsafe { fnGetMetadataHybridDimensionIdx(id, hybrid_idx) };
136 let name = format_dimension_member(
137 &format_lookup(
138 &unsafe { CStr::from_ptr(fnGetMetadataHybridName(id, hybrid_idx)) }
139 .to_string_lossy(),
140 ),
141 dimension_idx,
142 );
143 members.insert(
144 name.clone(),
145 Member {
146 name,
147 member_type: MemberType::Hybrid,
148 index: hybrid_idx,
149 enum_names: HashMap::new(),
150 },
151 );
152 }
153
154 metadata_vec.push(Metadata { id, name, members });
155 }
156 metadata_vec
157});
158
159pub(crate) fn format_lookup(name: &str) -> String {
160 name.replace(" ", "").to_lowercase()
161}
162
163fn format_dimension_member(name: &str, dim_idx: i32) -> String {
164 if dim_idx >= 0 {
165 let dim_suffix = ['x', 'y', 'z', 'w'];
166 let suffix = dim_suffix[dim_idx as usize];
167 format!("{name}{suffix}")
168 } else {
169 name.to_string()
170 }
171}
172
173pub trait MemberValue {
174 const TYPE: MemberType;
175
176 fn apply(&self, node: &mut Node, member: &Member) -> Result<(), FastNoiseError>;
177
178 fn invalid_member_type_error(member: &Member) -> FastNoiseError {
179 FastNoiseError::InvalidMemberType {
180 member_name: member.name.clone(),
181 expected: member.member_type,
182 found: Self::TYPE,
183 }
184 }
185}
186
187impl MemberValue for f32 {
188 const TYPE: MemberType = MemberType::Float;
189
190 fn apply(&self, node: &mut Node, member: &Member) -> Result<(), FastNoiseError> {
191 match member.member_type {
192 MemberType::Float => {
193 if !unsafe { fnSetVariableFloat(node.handle, member.index, *self) } {
194 return Err(FastNoiseError::SetFloatFailed);
195 }
196 }
197 MemberType::Hybrid => {
198 if !unsafe { fnSetHybridFloat(node.handle, member.index, *self) } {
199 return Err(FastNoiseError::SetHybridFloatFailed);
200 }
201 }
202 _ => return Err(Self::invalid_member_type_error(member)),
203 }
204 Ok(())
205 }
206}
207
208impl MemberValue for i32 {
209 const TYPE: MemberType = MemberType::Int;
210
211 fn apply(&self, node: &mut Node, member: &Member) -> Result<(), FastNoiseError> {
212 match member.member_type {
213 MemberType::Int => {
214 if !unsafe { fnSetVariableIntEnum(node.handle, member.index, *self) } {
215 return Err(FastNoiseError::SetIntFailed);
216 }
217 }
218 _ => return Err(Self::invalid_member_type_error(member)),
219 }
220 Ok(())
221 }
222}
223
224impl MemberValue for &str {
225 const TYPE: MemberType = MemberType::Enum;
226
227 fn apply(&self, node: &mut Node, member: &Member) -> Result<(), FastNoiseError> {
228 match member.member_type {
229 MemberType::Enum => {
230 let enum_idx = member.enum_names.get(&format_lookup(self)).ok_or_else(|| {
231 FastNoiseError::EnumValueNotFound {
232 expected: member.enum_names.keys().cloned().collect(),
233 found: self.to_string(),
234 }
235 })?;
236 if !unsafe { fnSetVariableIntEnum(node.handle, member.index, *enum_idx) } {
237 return Err(FastNoiseError::SetEnumFailed);
238 }
239 }
240 _ => return Err(Self::invalid_member_type_error(member)),
241 }
242 Ok(())
243 }
244}
245
246impl MemberValue for &Node {
247 const TYPE: MemberType = MemberType::NodeLookup;
248
249 fn apply(&self, node: &mut Node, member: &Member) -> Result<(), FastNoiseError> {
250 match member.member_type {
251 MemberType::NodeLookup => {
252 if !unsafe {
255 fnSetNodeLookup(
256 node.handle,
257 member.index,
258 &self.handle as *const _ as *const _,
259 )
260 } {
261 return Err(FastNoiseError::SetNodeLookupFailed);
262 }
263 }
264 MemberType::Hybrid => {
265 if !unsafe {
267 fnSetHybridNodeLookup(
268 node.handle,
269 member.index,
270 &self.handle as *const _ as *const _,
271 )
272 } {
273 return Err(FastNoiseError::SetHybridNodeLookupFailed);
274 }
275 }
276 _ => return Err(Self::invalid_member_type_error(member)),
277 }
278 Ok(())
279 }
280}