ankou 0.0.2

An OSINT repo miner focused on high-sev security bug in JS engines
Documentation
pub trait TypeInfo {
    fn type_name() -> String;
    fn type_of(&self) -> String;
}

#[allow(unused_macros)]
macro_rules! impl_type_info {
    ($($name:ident$(<$($T:ident),+>)*),*) => {
        $(impl_type_info_single!($name$(<$($T),*>)*);)*
    };
}

#[allow(unused_macros)]
macro_rules! mut_if {
    ($name:ident = $value:expr, $($any:expr)+) => {
        let mut $name = $value;
    };
    ($name:ident = $value:expr,) => {
        let $name = $value;
    };
}

#[allow(unused_macros)]
macro_rules! impl_type_info_single {
    ($name:ident$(<$($T:ident),+>)*) => {
        impl$(<$($T: TypeInfo),*>)* TypeInfo for $name$(<$($T),*>)* {
            fn type_name() -> String {
                mut_if!(res = String::from(stringify!($name)), $($($T)*)*);
                $(
                    res.push('<');
                    $(
                        res.push_str(&$T::type_name());
                        res.push(',');
                    )*
                    res.pop();
                    res.push('>');
                )*
                res
            }
            fn type_of(&self) -> String {
                $name$(::<$($T),*>)*::type_name()
            }
        }
    }
}

impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a T {
    fn type_name() -> String {
        let mut res = String::from("&");
        res.push_str(&T::type_name());
        res
    }
    fn type_of(&self) -> String {
        <&T>::type_name()
    }
}

impl<'a, T: TypeInfo + ?Sized> TypeInfo for &'a mut T {
    fn type_name() -> String {
        let mut res = String::from("&mut ");
        res.push_str(&T::type_name());
        res
    }
    fn type_of(&self) -> String {
        <&mut T>::type_name()
    }
}

#[allow(unused_macros)]
macro_rules! type_of {
    ($x:expr) => {
        (&$x).type_of()
    };
}

#[cfg(test)]
mod tests {
    use super::*;
    use crate::git::Repository;

    impl_type_info!(i32, i64, f32, f64, str, String, Vec<T>, Result<T,S>, Repository);

    #[test]
    fn i32() {
        assert_eq!(type_of!(1 as i32), type_of!("1".parse::<i32>().unwrap()));
    }

    #[test]
    fn i64() {
        assert_eq!(type_of!(1 as i64), type_of!("1".parse::<i64>().unwrap()));
    }

    #[test]
    fn f32() {
        assert_eq!(type_of!(1 as f32), type_of!("1".parse::<f32>().unwrap()));
    }

    #[test]
    fn f64() {
        assert_eq!(type_of!(1 as f64), type_of!("1".parse::<f64>().unwrap()));
    }

    #[test]
    fn str() {
        assert_eq!(type_of!("1"), type_of!(String::from("1").as_str()));
    }

    #[test]
    fn string() {
        assert_eq!(type_of!("1".to_string()), type_of!(String::from("1")));
    }

    #[test]
    fn vec_generic() {
        assert_eq!(type_of!(vec![1]), type_of!(Vec::new() as Vec<i32>));
    }

    #[test]
    fn result_generic() {
        let r: Result<i32, i32> = Ok(1);
        let e: Result<i32, i32> = Err(2);
        assert_eq!(type_of!(r), type_of!(e));
    }

    #[test]
    fn git_repository() {
        use ::std::{env, fs, path};
        let current_repo_path: path::PathBuf = match env::var("CARGO_MANIFEST_DIR") {
            Ok(p) => path::PathBuf::from(p),
            Err(e) => panic!("Could not access CARGO_MANIFEST_DIR environment variable, Err: {:?}", e),
        };
        let test_init_repo_path: path::PathBuf = current_repo_path
            .clone()
            .join("tests")
            .join("akty");

        let r1: Repository = match Repository::discover(current_repo_path) {
            Ok(r) => r,
            Err(e) => panic!("Error discovering current repository, Err: {:?}", e),
        };
        
        let r2: Repository = match Repository::init(test_init_repo_path.clone()) {
            Ok(r) => r,
            Err(e) => panic!("Error initialize tests/ak_type-of repository, Err: {:?}", e),
        };
    
        assert_eq!(type_of!(r1), type_of!(r2));
        fs::remove_dir_all(test_init_repo_path).unwrap();
    }
}