fyrox_core/
lib.rs

1// Copyright (c) 2019-present Dmitry Stepanov and Fyrox Engine contributors.
2//
3// Permission is hereby granted, free of charge, to any person obtaining a copy
4// of this software and associated documentation files (the "Software"), to deal
5// in the Software without restriction, including without limitation the rights
6// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7// copies of the Software, and to permit persons to whom the Software is
8// furnished to do so, subject to the following conditions:
9//
10// The above copyright notice and this permission notice shall be included in all
11// copies or substantial portions of the Software.
12//
13// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
19// SOFTWARE.
20
21//! Core data structures and algorithms used throughout Fyrox.
22//!
23//! Some of them can be useful separately outside the engine.
24
25#![allow(clippy::upper_case_acronyms)]
26#![allow(clippy::from_over_into)]
27#![allow(clippy::doc_lazy_continuation)]
28#![allow(mismatched_lifetime_syntaxes)]
29
30#[macro_use]
31extern crate memoffset;
32#[macro_use]
33extern crate lazy_static;
34
35pub use arrayvec;
36pub use byteorder;
37pub use nalgebra as algebra;
38pub use num_traits;
39pub use parking_lot;
40pub use rand;
41pub use sstorage::ImmutableString;
42pub use uuid;
43
44use crate::visitor::{Visit, VisitResult, Visitor};
45use bytemuck::Pod;
46use fxhash::FxHashMap;
47pub use safelock::*;
48use std::collections::hash_map::Entry;
49use std::ffi::OsString;
50use std::hash::Hasher;
51use std::{
52    borrow::Borrow,
53    cmp,
54    hash::Hash,
55    path::{Path, PathBuf},
56};
57pub mod color;
58pub mod color_gradient;
59pub mod early;
60pub mod io;
61pub mod log;
62pub mod math;
63pub mod net;
64pub mod numeric_range;
65pub mod platform;
66pub mod pool;
67pub mod quadtree;
68pub mod rectpack;
69pub mod reflect;
70mod safelock;
71pub mod sparse;
72pub mod sstorage;
73pub mod task;
74pub mod type_traits;
75pub mod variable;
76pub mod visitor;
77pub mod watcher;
78
79pub use futures;
80pub use instant;
81
82pub use notify;
83
84#[cfg(target_arch = "wasm32")]
85pub use js_sys;
86use std::marker::PhantomData;
87#[cfg(target_arch = "wasm32")]
88pub use wasm_bindgen;
89#[cfg(target_arch = "wasm32")]
90pub use wasm_bindgen_futures;
91#[cfg(target_arch = "wasm32")]
92pub use web_sys;
93
94pub use type_traits::prelude::*;
95/// Defines as_(variant), as_mut_(variant) and is_(variant) methods.
96#[macro_export]
97macro_rules! define_is_as {
98    ($typ:tt : $kind:ident -> ref $result:path => fn $is:ident, fn $as_ref:ident, fn $as_mut:ident) => {
99        /// Returns true if node is instance of given type.
100        pub fn $is(&self) -> bool {
101            match self {
102                $typ::$kind(_) => true,
103                _ => false,
104            }
105        }
106
107        /// Tries to cast shared reference to a node to given type, panics if
108        /// cast is not possible.
109        pub fn $as_ref(&self) -> &$result {
110            match self {
111                $typ::$kind(ref val) => val,
112                _ => panic!("Cast to {} failed!", stringify!($kind)),
113            }
114        }
115
116        /// Tries to cast mutable reference to a node to given type, panics if
117        /// cast is not possible.
118        pub fn $as_mut(&mut self) -> &mut $result {
119            match self {
120                $typ::$kind(ref mut val) => val,
121                _ => panic!("Cast to {} failed!", stringify!($kind)),
122            }
123        }
124    };
125}
126
127/// Utility function that replaces back slashes \ to forward /. Internally, it converts the input
128/// path to string (lossy - see [`Path::to_string_lossy`]) and replaces the slashes in the string.
129/// Finally, it converts the string to the PathBuf and returns it. This method is intended to be
130/// used only for paths, that does not contain non-unicode characters.
131pub fn replace_slashes<P: AsRef<Path>>(path: P) -> PathBuf {
132    PathBuf::from(
133        path.as_ref()
134            .to_string_lossy()
135            .to_string()
136            .replace('\\', "/"),
137    )
138}
139
140/// Appends specified extension to the path.
141///
142/// # Examples
143///
144/// ```rust
145/// # use std::path::Path;
146/// # use fyrox_core::append_extension;
147/// let path = Path::new("foo.bar");
148/// let new_path = append_extension(path, "baz");
149/// assert_eq!(new_path, Path::new("foo.bar.baz"))
150/// ```
151#[must_use]
152pub fn append_extension<P: AsRef<Path>, E: AsRef<str>>(
153    path: P,
154    additional_extension: E,
155) -> PathBuf {
156    let mut final_path = path.as_ref().to_path_buf();
157    let new_extension = final_path
158        .extension()
159        .map(|e| {
160            let mut ext = e.to_owned();
161            ext.push(".");
162            ext.push(additional_extension.as_ref());
163            ext
164        })
165        .unwrap_or_else(|| OsString::from(additional_extension.as_ref()));
166    final_path.set_extension(new_extension);
167    final_path
168}
169
170#[derive(Clone, Debug)]
171pub struct BiDirHashMap<K, V> {
172    forward_map: FxHashMap<K, V>,
173    backward_map: FxHashMap<V, K>,
174}
175
176impl<K: Hash + Eq + Clone, V: Hash + Eq + Clone> BiDirHashMap<K, V> {
177    pub fn insert(&mut self, key: K, value: V) -> Option<V> {
178        let existing = self.forward_map.insert(key.clone(), value.clone());
179        self.backward_map.insert(value, key);
180        existing
181    }
182
183    pub fn remove_by_key(&mut self, key: &K) -> Option<V> {
184        if let Some(value) = self.forward_map.remove(key) {
185            self.backward_map.remove(&value);
186            Some(value)
187        } else {
188            None
189        }
190    }
191
192    pub fn contains_key<Q: ?Sized + Hash + Eq>(&self, key: &Q) -> bool
193    where
194        K: Borrow<Q>,
195    {
196        self.forward_map.contains_key(key)
197    }
198
199    pub fn remove_by_value(&mut self, value: &V) -> Option<K> {
200        if let Some(key) = self.backward_map.remove(value) {
201            self.forward_map.remove(&key);
202            Some(key)
203        } else {
204            None
205        }
206    }
207
208    pub fn contains_value<Q: ?Sized + Hash + Eq>(&self, value: &Q) -> bool
209    where
210        V: Borrow<Q>,
211    {
212        self.backward_map.contains_key(value)
213    }
214
215    pub fn value_of(&self, node: &K) -> Option<&V> {
216        self.forward_map.get(node)
217    }
218
219    pub fn key_of(&self, value: &V) -> Option<&K> {
220        self.backward_map.get(value)
221    }
222
223    pub fn len(&self) -> usize {
224        self.forward_map.len()
225    }
226
227    pub fn is_empty(&self) -> bool {
228        self.forward_map.is_empty()
229    }
230
231    pub fn clear(&mut self) {
232        self.forward_map.clear();
233        self.backward_map.clear();
234    }
235
236    pub fn forward_map(&self) -> &FxHashMap<K, V> {
237        &self.forward_map
238    }
239
240    pub fn backward_map(&self) -> &FxHashMap<V, K> {
241        &self.backward_map
242    }
243
244    pub fn into_inner(self) -> (FxHashMap<K, V>, FxHashMap<V, K>) {
245        (self.forward_map, self.backward_map)
246    }
247}
248
249impl<K, V> Default for BiDirHashMap<K, V> {
250    fn default() -> Self {
251        Self {
252            forward_map: Default::default(),
253            backward_map: Default::default(),
254        }
255    }
256}
257
258impl<K: Hash + Eq + Clone, V: Hash + Eq + Clone> From<FxHashMap<K, V>> for BiDirHashMap<K, V> {
259    fn from(forward_map: FxHashMap<K, V>) -> Self {
260        let mut backward_map = FxHashMap::default();
261        for (k, v) in forward_map.iter() {
262            backward_map.insert(v.clone(), k.clone());
263        }
264        Self {
265            forward_map,
266            backward_map,
267        }
268    }
269}
270
271impl<K, V> Visit for BiDirHashMap<K, V>
272where
273    K: Hash + Eq + Clone + Default + Visit,
274    V: Hash + Eq + Clone + Default + Visit,
275{
276    fn visit(&mut self, name: &str, visitor: &mut Visitor) -> VisitResult {
277        let mut region = visitor.enter_region(name)?;
278
279        self.forward_map.visit("ForwardMap", &mut region)?;
280        self.backward_map.visit("BackwardMap", &mut region)?;
281
282        Ok(())
283    }
284}
285
286impl<K, V> FromIterator<(K, V)> for BiDirHashMap<K, V>
287where
288    K: Hash + Eq + Clone,
289    V: Hash + Eq + Clone,
290{
291    fn from_iter<T: IntoIterator<Item = (K, V)>>(iter: T) -> Self {
292        let mut hm = Self::default();
293        for (k, v) in iter {
294            hm.forward_map.insert(k.clone(), v.clone());
295            hm.backward_map.insert(v, k);
296        }
297        hm
298    }
299}
300
301#[inline]
302pub fn hash_combine(lhs: u64, rhs: u64) -> u64 {
303    lhs ^ (rhs
304        .wrapping_add(0x9e3779b9)
305        .wrapping_add(lhs << 6)
306        .wrapping_add(lhs >> 2))
307}
308
309/// Strip working directory from file name. The function may fail for one main reason -
310/// input path is not valid, does not exist, or there is some other issues with it.
311/// The last component of the path is permitted to not exist, so long as the rest of the path exists.
312pub fn make_relative_path<P: AsRef<Path>>(path: P) -> Result<PathBuf, std::io::Error> {
313    let path = path.as_ref();
314    // Canonicalization requires the full path to exist, so remove the file name before
315    // calling canonicalize.
316    let file_name = path.file_name().ok_or(std::io::Error::new(
317        std::io::ErrorKind::InvalidData,
318        format!("Invalid path: {}", path.display()),
319    ))?;
320    let dir = path.parent();
321    let dir = if let Some(dir) = dir {
322        if dir.as_os_str().is_empty() {
323            Path::new(".")
324        } else {
325            dir
326        }
327    } else {
328        Path::new(".")
329    };
330    let canon_path = dir
331        .canonicalize()
332        .map_err(|err| {
333            std::io::Error::other(format!(
334                "Unable to canonicalize '{}'. Reason: {err}",
335                dir.display()
336            ))
337        })?
338        .join(file_name);
339    match canon_path.strip_prefix(std::env::current_dir()?.canonicalize()?) {
340        Ok(relative_path) => Ok(replace_slashes(relative_path)),
341        Err(err) => Err(std::io::Error::other(format!(
342            "unable to strip prefix from '{}'! Reason: {err}",
343            canon_path.display()
344        ))),
345    }
346}
347
348/// "Transmutes" array of any sized type to a slice of bytes.
349pub fn array_as_u8_slice<T: Sized + Pod>(v: &[T]) -> &'_ [u8] {
350    // SAFETY: It is safe to reinterpret data to read it.
351    unsafe { std::slice::from_raw_parts(v.as_ptr() as *const u8, std::mem::size_of_val(v)) }
352}
353
354/// "Transmutes" array of any sized type to a slice of bytes.
355pub fn array_as_u8_slice_mut<T: Sized + Pod>(v: &mut [T]) -> &'_ mut [u8] {
356    // SAFETY: It is safe to reinterpret memory region of POD type.
357    unsafe { std::slice::from_raw_parts_mut(v.as_mut_ptr() as *mut u8, std::mem::size_of_val(v)) }
358}
359
360/// "Transmutes" array of any sized type to a slice of some other type.
361pub fn transmute_slice<T: Sized, U: Sized>(v: &[T]) -> &'_ [U] {
362    // SAFETY: It is safe to reinterpret data to read it.
363    unsafe {
364        std::slice::from_raw_parts(
365            v.as_ptr() as *const U,
366            std::mem::size_of_val(v) / std::mem::size_of::<U>(),
367        )
368    }
369}
370
371/// "Transmutes" value of any sized type to a slice of bytes.
372pub fn value_as_u8_slice<T: Sized + Pod>(v: &T) -> &'_ [u8] {
373    // SAFETY: It is safe to reinterpret data to read it.
374    unsafe { std::slice::from_raw_parts(v as *const T as *const u8, std::mem::size_of::<T>()) }
375}
376
377/// Takes a vector of trivially-copyable values and turns it into a vector of bytes.
378pub fn transmute_vec_as_bytes<T: Pod>(vec: Vec<T>) -> Vec<u8> {
379    unsafe {
380        let mut vec = std::mem::ManuallyDrop::new(vec);
381        Vec::from_raw_parts(
382            vec.as_mut_ptr() as *mut u8,
383            vec.len() * std::mem::size_of::<T>(),
384            vec.capacity() * std::mem::size_of::<T>(),
385        )
386    }
387}
388
389/// Performs hashing of a sized value by interpreting it as raw memory.
390pub fn hash_as_bytes<T: Sized + Pod, H: Hasher>(value: &T, hasher: &mut H) {
391    hasher.write(value_as_u8_slice(value))
392}
393
394/// Compares two strings using case-insensitive comparison. This function does not allocate any
395/// any memory and significantly faster than `a.to_lowercase() == b.to_lowercase()`.
396pub fn cmp_strings_case_insensitive(a: impl AsRef<str>, b: impl AsRef<str>) -> bool {
397    let a_ref = a.as_ref();
398    let b_ref = b.as_ref();
399
400    if a_ref.len() != b_ref.len() {
401        return false;
402    }
403
404    a_ref
405        .chars()
406        .zip(b_ref.chars())
407        .all(|(ca, cb)| ca.to_lowercase().eq(cb.to_lowercase()))
408}
409
410pub fn make_pretty_type_name(type_name: &str) -> &str {
411    let mut colon_position = None;
412    let mut byte_pos = 0;
413    for c in type_name.chars() {
414        byte_pos += c.len_utf8();
415        if c == ':' {
416            colon_position = Some(byte_pos);
417        } else if c == '<' {
418            break;
419        }
420    }
421    if let Some(colon_position) = colon_position {
422        type_name.split_at(colon_position).1
423    } else {
424        type_name
425    }
426}
427
428#[repr(transparent)]
429#[derive(Debug)]
430pub struct PhantomDataSendSync<T: ?Sized>(PhantomData<T>);
431
432// SAFETY: PhantomDataSendSync does not hold any data.
433unsafe impl<T: ?Sized> Send for PhantomDataSendSync<T> {}
434// SAFETY: PhantomDataSendSync does not hold any data.
435unsafe impl<T: ?Sized> Sync for PhantomDataSendSync<T> {}
436
437impl<T: ?Sized> Hash for PhantomDataSendSync<T> {
438    #[inline]
439    fn hash<H: Hasher>(&self, _: &mut H) {}
440}
441
442impl<T: ?Sized> PartialEq for PhantomDataSendSync<T> {
443    fn eq(&self, _other: &PhantomDataSendSync<T>) -> bool {
444        true
445    }
446}
447
448impl<T: ?Sized> Eq for PhantomDataSendSync<T> {}
449
450impl<T: ?Sized> PartialOrd for PhantomDataSendSync<T> {
451    fn partial_cmp(&self, _other: &PhantomDataSendSync<T>) -> Option<cmp::Ordering> {
452        Some(self.cmp(_other))
453    }
454}
455
456impl<T: ?Sized> Ord for PhantomDataSendSync<T> {
457    fn cmp(&self, _other: &PhantomDataSendSync<T>) -> cmp::Ordering {
458        cmp::Ordering::Equal
459    }
460}
461
462impl<T: ?Sized> Copy for PhantomDataSendSync<T> {}
463
464impl<T: ?Sized> Clone for PhantomDataSendSync<T> {
465    fn clone(&self) -> Self {
466        *self
467    }
468}
469
470impl<T: ?Sized> Default for PhantomDataSendSync<T> {
471    fn default() -> Self {
472        Self(PhantomData)
473    }
474}
475
476/// A trait for entities that have name.
477pub trait NameProvider {
478    /// Returns a reference to the name of the entity.
479    fn name(&self) -> &str;
480}
481
482/// Tries to find an entity by its name in a series of entities produced by an iterator.
483pub fn find_by_name_ref<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a T)>
484where
485    T: NameProvider,
486    I: Iterator<Item = (K, &'a T)>,
487    S: AsRef<str>,
488{
489    iter.find(|(_, value)| value.name() == name.as_ref())
490}
491
492/// Tries to find an entity by its name in a series of entities produced by an iterator.
493pub fn find_by_name_mut<'a, T, I, S, K>(mut iter: I, name: S) -> Option<(K, &'a mut T)>
494where
495    T: NameProvider,
496    I: Iterator<Item = (K, &'a mut T)>,
497    S: AsRef<str>,
498{
499    iter.find(|(_, value)| value.name() == name.as_ref())
500}
501
502/// Swaps the content of a hash map entry with the content of an `Option`.
503pub fn swap_hash_map_entry<K, V>(entry: Entry<K, V>, value: &mut Option<V>) {
504    match (entry, value) {
505        (Entry::Occupied(entry), p @ None) => *p = Some(entry.remove()),
506        (Entry::Occupied(mut entry), Some(p)) => std::mem::swap(entry.get_mut(), p),
507        (Entry::Vacant(_), None) => (),
508        (Entry::Vacant(entry), p @ Some(_)) => drop(entry.insert(p.take().unwrap())),
509    }
510}
511
512/// Swaps the content of two hash map entries.
513pub fn swap_hash_map_entries<K0, K1, V>(entry0: Entry<K0, V>, entry1: Entry<K1, V>) {
514    match (entry0, entry1) {
515        (Entry::Occupied(e0), Entry::Vacant(e1)) => drop(e1.insert(e0.remove())),
516        (Entry::Occupied(mut e0), Entry::Occupied(mut e1)) => {
517            std::mem::swap(e0.get_mut(), e1.get_mut())
518        }
519        (Entry::Vacant(_), Entry::Vacant(_)) => (),
520        (Entry::Vacant(e0), Entry::Occupied(e1)) => drop(e0.insert(e1.remove())),
521    }
522}
523
524#[macro_export]
525macro_rules! define_as_any_trait {
526    ($trait_name:ident => $derived_trait:ident) => {
527        /// A base trait that provides useful methods for trait downcasting.
528        pub trait $trait_name: std::any::Any {
529            /// Casts `self` as `&dyn Any`.
530            fn as_any(&self) -> &dyn std::any::Any;
531
532            /// Casts `self` as `&mut dyn Any`.
533            fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
534
535            /// Casts `Box<Self>` as `Box<dyn Any>`.
536            fn into_any(self: Box<Self>) -> Box<dyn std::any::Any>;
537        }
538
539        impl<T: $derived_trait> $trait_name for T {
540            fn as_any(&self) -> &dyn std::any::Any {
541                self
542            }
543
544            fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
545                self
546            }
547
548            fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
549                self
550            }
551        }
552    };
553    ($trait_name:ident: $($sub_trait:ident),* => $derived_trait:ident) => {
554        /// A base trait that provides useful methods for trait downcasting.
555        pub trait $trait_name: std::any::Any + $($sub_trait),* {
556            fn as_any(&self) -> &dyn std::any::Any;
557
558            fn as_any_mut(&mut self) -> &mut dyn std::any::Any;
559
560            fn into_any(self: Box<Self>) -> Box<dyn std::any::Any>;
561        }
562
563        impl<T: $derived_trait> $trait_name for T {
564            fn as_any(&self) -> &dyn std::any::Any {
565                self
566            }
567
568            fn as_any_mut(&mut self) -> &mut dyn std::any::Any {
569                self
570            }
571
572            fn into_any(self: Box<Self>) -> Box<dyn std::any::Any> {
573                self
574            }
575        }
576    };
577}
578
579#[cfg(test)]
580mod test {
581    use std::path::Path;
582
583    use crate::{
584        append_extension, cmp_strings_case_insensitive, combine_uuids, hash_combine,
585        make_relative_path, transmute_vec_as_bytes,
586        visitor::{Visit, Visitor},
587        BiDirHashMap,
588    };
589    use fxhash::FxHashMap;
590    use std::mem::size_of;
591    use uuid::uuid;
592
593    #[test]
594    fn test_combine_uuids() {
595        let a = uuid!("d1a45bd5-5066-4b28-b103-95c59c230e77");
596        let b = uuid!("0a06591a-1c66-4299-ba6f-2b205b795575");
597
598        assert_ne!(combine_uuids(a, b), a);
599        assert_ne!(combine_uuids(a, b), b);
600    }
601
602    #[test]
603    fn test_append_extension() {
604        let path = Path::new("foo.bar");
605        let new_path = append_extension(path, "baz");
606        assert_eq!(new_path, Path::new("foo.bar.baz"));
607    }
608
609    #[test]
610    fn bi_dir_hash_map_insert() {
611        let mut map = BiDirHashMap::<u32, u32>::default();
612
613        assert!(map.forward_map.is_empty());
614        assert!(map.backward_map.is_empty());
615
616        let result = map.insert(1, 42);
617
618        assert_eq!(result, None);
619        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
620        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
621    }
622
623    #[test]
624    fn bi_dir_hash_map_remove_by_key() {
625        let mut map = BiDirHashMap::<u32, u32>::default();
626        map.insert(1, 42);
627
628        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
629        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
630
631        let result = map.remove_by_key(&42);
632        assert_eq!(result, None);
633
634        let result = map.remove_by_key(&1);
635        assert_eq!(result, Some(42));
636        assert!(map.forward_map.is_empty());
637        assert!(map.backward_map.is_empty());
638    }
639
640    #[test]
641    fn bi_dir_hash_map_remove_by_value() {
642        let mut map = BiDirHashMap::<u32, u32>::default();
643        map.insert(1, 42);
644
645        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
646        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
647
648        let result = map.remove_by_value(&1);
649        assert_eq!(result, None);
650
651        let result = map.remove_by_value(&42);
652        assert_eq!(result, Some(1));
653        assert!(map.forward_map.is_empty());
654        assert!(map.backward_map.is_empty());
655    }
656
657    #[test]
658    fn bi_dir_hash_map_contains_key() {
659        let mut map = BiDirHashMap::<u32, u32>::default();
660        map.insert(1, 42);
661
662        assert!(map.contains_key(&1));
663        assert!(!map.contains_key(&42));
664    }
665
666    #[test]
667    fn bi_dir_hash_map_contains_value() {
668        let mut map = BiDirHashMap::<u32, u32>::default();
669        map.insert(1, 42);
670
671        assert!(map.contains_value(&42));
672        assert!(!map.contains_value(&1));
673    }
674
675    #[test]
676    fn bi_dir_hash_map_value_of() {
677        let mut map = BiDirHashMap::<u32, u32>::default();
678        map.insert(1, 42);
679
680        assert_eq!(map.value_of(&1), Some(&42));
681        assert_eq!(map.value_of(&42), None);
682    }
683
684    #[test]
685    fn bi_dir_hash_map_key_of() {
686        let mut map = BiDirHashMap::<u32, u32>::default();
687        map.insert(1, 42);
688
689        assert_eq!(map.key_of(&1), None);
690        assert_eq!(map.key_of(&42), Some(&1));
691    }
692
693    #[test]
694    fn bi_dir_hash_map_getters() {
695        let mut map = BiDirHashMap::<u32, u32>::default();
696        assert!(map.is_empty());
697
698        map.insert(1, 42);
699        assert_eq!(map.len(), 1);
700
701        assert!(map.forward_map().eq(&map.forward_map));
702        assert!(map.backward_map().eq(&map.backward_map));
703
704        map.clear();
705        assert!(map.is_empty());
706    }
707
708    #[test]
709    fn bi_dir_hash_map_into_inner() {
710        let mut map = BiDirHashMap::<u32, u32>::default();
711        map.insert(1, 42);
712
713        let (f, b) = map.clone().into_inner();
714        assert!(map.forward_map().eq(&f));
715        assert!(map.backward_map().eq(&b));
716    }
717
718    #[test]
719    fn from_fx_hash_map_for_bi_dir_hash_map() {
720        let mut h = FxHashMap::default();
721        h.insert(1, 42);
722
723        let map = BiDirHashMap::from(h);
724        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
725        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
726    }
727
728    #[test]
729    fn test_visit_for_bi_dir_hash_map() {
730        let mut map = BiDirHashMap::<u32, u32>::default();
731        let mut visitor = Visitor::default();
732
733        assert!(map.visit("name", &mut visitor).is_ok());
734    }
735
736    #[test]
737    fn from_iter_for_bi_dir_hash_map() {
738        let map = BiDirHashMap::from_iter(vec![(1, 42)]);
739
740        assert_eq!(map.forward_map.get_key_value(&1), Some((&1, &42)));
741        assert_eq!(map.backward_map.get_key_value(&42), Some((&42, &1)));
742    }
743
744    #[test]
745    fn test_hash_combine() {
746        assert_eq!(hash_combine(1, 1), 0x9E3779FB);
747    }
748
749    #[test]
750    fn test_make_relative_path() {
751        assert!(make_relative_path(Path::new("fake_dir").join(Path::new("foo.txt"))).is_err());
752        make_relative_path(Path::new("Cargo.toml")).unwrap();
753        make_relative_path(Path::new("Cargo.toml").canonicalize().unwrap()).unwrap();
754    }
755
756    #[test]
757    fn tests_case_insensitive_str_comparison() {
758        assert!(cmp_strings_case_insensitive("FooBar", "FOOBaR"));
759        assert!(!cmp_strings_case_insensitive("FooBaz", "FOOBaR"));
760        assert!(cmp_strings_case_insensitive("foobar", "foobar"));
761    }
762
763    #[test]
764    fn test_transmute_vec_as_bytes_length_new_f32() {
765        let vec = vec![1.0f32, 2.0, 3.0];
766        let byte_vec = transmute_vec_as_bytes(vec.clone());
767        let expected_length = vec.len() * size_of::<f32>();
768        assert_eq!(byte_vec.len(), expected_length);
769    }
770
771    #[test]
772    fn test_transmute_vec_as_bytes_length_new_usize() {
773        let vec = vec![1usize, 2, 3];
774        let byte_vec = transmute_vec_as_bytes(vec.clone());
775        let expected_length = vec.len() * size_of::<usize>();
776        assert_eq!(byte_vec.len(), expected_length);
777    }
778}