Skip to main content

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