1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
#[macro_use]
pub mod core;

pub mod enc;
pub mod errors;
pub mod net;
pub mod sys;
pub mod unit;

/// Types exported directly into the fungus namespace
pub use crate::errors::FuError;
pub use crate::errors::FuResult;

/// All essential symbols in a simple consumable way
///
/// ### Examples
/// ```
/// use fungus::prelude::*;
/// ```
pub mod prelude {
    pub use super::{
        cfgblock,
        core::*,
        defer,
        enc::{gzip, tar},
        errors::*,
        function,
        net::{self, agent},
        sys::{self, exec, ext::*, user},
        unit::{self, time},
    };

    // Re-exports
    //----------------------------------------------------------------------------------------------
    pub use blake2;
    pub use gory::*;
    pub use lazy_static::*;
    pub use libc;
    pub use regex::Regex;
    pub use std::{
        ffi::{OsStr, OsString},
        fmt,
        fs::{self, File, OpenOptions},
        io::{self, prelude::*, BufRead, BufReader},
        os::unix::fs::{MetadataExt, PermissionsExt},
        path::{Path, PathBuf},
        str,
    };
}

/// All essential symbols for testing in a simple consumable way
///
/// ### Examples
/// ```
/// use fungus::assert::*;
/// ```
pub mod assert {
    pub use super::{assert_dir, assert_exists, assert_file, assert_no_dir, assert_no_exists, assert_no_file, assert_remove, assert_remove_all, assert_setup, core::assert::*, create_test_setup_func, prelude::*};
}

/// Ensure the given closure is executed once the surrounding scope closes despite panics.
/// Inspired by Golang's `defer`, Java's finally and Ruby's `ensure`.
///
/// This provides a mechanism similar to Golang's `defer` that will trigger when the
/// surrounding function goes out of scope.
///
/// ### Examples
/// ```
/// use fungus::prelude::*;
///
/// let tmpdir = PathBuf::from("tests/temp").abs().unwrap().mash("core_defer_doc");
/// assert!(sys::remove_all(&tmpdir).is_ok());
/// assert!(sys::mkdir(&tmpdir).is_ok());
///
/// // Create a scope that will trigger finally's destructor
/// {
///     defer!(sys::remove_all(&tmpdir).unwrap());
/// }
/// assert_eq!(tmpdir.exists(), false);
/// ```
#[macro_export]
macro_rules! defer {
    ($($tokens:tt)*) => {
        let _defer = defer(|| { $($tokens)* });
    };
}

/// Expands to a string literal of the current function's name similar to the
/// venerable `file!` or `line!` https://github.com/rust-lang/rfcs/pull/1719.
///
/// ### Examples
/// ```
/// use fungus::prelude::*;
///
/// fn my_func() -> &'static str {
///     function!()
/// }
/// assert_eq!(my_func(), "my_func");
/// ```
#[macro_export]
macro_rules! function {
    () => {{
        // Capture the function's type and passes it to `std::any::type_name` to get the
        // function's fully qualified name, which includes our target.
        // https://doc.rust-lang.org/std/any/fn.type_name.html
        fn _f() {}
        fn type_of<T>(_: T) -> &'static str {
            std::any::type_name::<T>()
        }

        // Capture the fully qualified name
        let fqn = type_of(_f);

        // Trim off the suffix
        let fqn = &fqn[..fqn.len() - 4];

        // Trim off the prefix if it exists
        match fqn.rfind(':') {
            Some(i) => &fqn[i + 1..],
            None => &fqn,
        }
    }};
}

// Unit tests
// -------------------------------------------------------------------------------------------------
#[cfg(test)]
mod tests {
    use crate::prelude::*;
    use std::cell::Cell;

    #[test]
    fn test_defer_macro() {
        let obj = Cell::new(1);
        defer!(obj.set(2));
        assert_eq!(1, obj.get());
    }

    #[test]
    fn test_function_macro() {
        fn indirect_func_name() -> &'static str {
            function!()
        }
        assert_eq!(function!(), "test_function_macro");
        assert_eq!(indirect_func_name(), "indirect_func_name");
    }
}