rust-code-analysis 0.0.25

Tool to compute and export code metrics
Documentation
macro_rules! mk_checker {
    ( $name:ident, $( $type:ident ),* ) => {
        #[inline(always)]
        #[allow(unused_variables)]
        fn $name(node: &Node) -> bool {
            let typ = node.object().kind_id();
            false
            $(
                || typ == <Self as TSLanguage>::BaseLang::$type
            )*
        }
    };
}

macro_rules! mk_else_if {
    ($if_type:ident) => {
        #[inline(always)]
        fn is_else_if(node: &Node) -> bool {
            if node.object().kind_id() != <Self as TSLanguage>::BaseLang::$if_type {
                return false;
            }
            if let Some(parent) = node.object().parent() {
                return node.object().kind_id() == <Self as TSLanguage>::BaseLang::$if_type
                    && parent.kind_id() == <Self as TSLanguage>::BaseLang::$if_type;
            }
            false
        }
    };
}

macro_rules! get_language {
    (tree_sitter_cpp) => {
        tree_sitter_mozcpp::language()
    };
    (tree_sitter_typescript) => {
        tree_sitter_typescript::language_typescript()
    };
    (tree_sitter_tsx) => {
        tree_sitter_typescript::language_tsx()
    };
    ($name:ident) => {
        $name::language()
    };
}

macro_rules! mk_enum {
    ( $( $camel:ident, $description:expr ),* ) => {
        /// The list of supported languages.
        #[derive(Clone, Copy, Debug, PartialEq, Eq)]
        pub enum LANG {
            $(
                #[doc = $description]
                $camel,
            )*
        }
        impl LANG {
            pub fn into_enum_iter() -> impl Iterator<Item=LANG> {
                use LANG::*;
                [$( $camel, )*].into_iter()
            }
        }
    };
}

macro_rules! mk_impl_lang {
    ( $( ($camel:ident, $name:ident, $display: expr) ),* ) => {
        impl LANG {

            /// Returns the name of a language as a `&str`.
            ///
            /// # Examples
            ///
            /// ```
            /// use rust_code_analysis::LANG;
            ///
            /// println!("{}", LANG::Rust.get_name());
            /// ```
            pub fn get_name(&self) -> &'static str {
                match self {
                    $(
                        LANG::$camel => $display,
                    )*
                }
            }
        }
    };
}

macro_rules! mk_action {
    ( $( ($camel:ident, $parser:ident) ),* ) => {
        /// Runs a function, which implements the [`Callback`] trait,
        /// on a code written in one of the supported languages.
        ///
        /// # Examples
        ///
        /// The following example dumps to shell every metric computed using
        /// the dummy source code.
        ///
        /// ```
        /// use std::path::PathBuf;
        ///
        /// use rust_code_analysis::{action, Callback, LANG, Metrics, MetricsCfg};
        ///
        /// let source_code = "int a = 42;";
        /// let language = LANG::Cpp;
        ///
        /// // The path to a dummy file used to contain the source code
        /// let path = PathBuf::from("foo.c");
        /// let source_as_vec = source_code.as_bytes().to_vec();
        ///
        /// // Configuration options used by the function which computes the metrics
        /// let cfg = MetricsCfg {
        ///     path,
        /// };
        ///
        /// action::<Metrics>(&language, source_as_vec, &cfg.path.clone(), None, cfg);
        /// ```
        ///
        /// [`Callback`]: trait.Callback.html
        #[inline(always)]
        pub fn action<T: Callback>(lang: &LANG, source: Vec<u8>, path: &Path, pr: Option<Arc<PreprocResults>>, cfg: T::Cfg) -> T::Res {
            match lang {
                $(
                    LANG::$camel => {
                        let parser = $parser::new(source, path, pr);
                        T::call(cfg, &parser)
                    },
                )*
            }
        }

        /// Returns all function spaces data of a code.
        ///
        /// # Examples
        ///
        /// ```
        /// use std::path::PathBuf;
        ///
        /// use rust_code_analysis::{get_function_spaces, LANG};
        ///
        /// let source_code = "int a = 42;";
        /// let language = LANG::Cpp;
        ///
        /// // The path to a dummy file used to contain the source code
        /// let path = PathBuf::from("foo.c");
        /// let source_as_vec = source_code.as_bytes().to_vec();
        ///
        /// get_function_spaces(&language, source_as_vec, &path, None).unwrap();
        /// ```
        #[inline(always)]
        pub fn get_function_spaces(lang: &LANG, source: Vec<u8>, path: &Path, pr: Option<Arc<PreprocResults>>) -> Option<FuncSpace> {
            match lang {
                $(
                    LANG::$camel => {
                        let parser = $parser::new(source, &path, pr);
                        metrics(&parser, &path)
                    },
                )*
            }
        }

        /// Returns all operators and operands of each space in a code.
        ///
        /// # Examples
        ///
        /// ```
        /// use std::path::PathBuf;
        ///
        /// use rust_code_analysis::{get_ops, LANG};
        ///
        /// # fn main() {
        /// let source_code = "int a = 42;";
        /// let language = LANG::Cpp;
        ///
        /// // The path to a dummy file used to contain the source code
        /// let path = PathBuf::from("foo.c");
        /// let source_as_vec = source_code.as_bytes().to_vec();
        ///
        /// get_ops(&language, source_as_vec, &path, None).unwrap();
        /// # }
        /// ```
        #[inline(always)]
        pub fn get_ops(lang: &LANG, source: Vec<u8>, path: &Path, pr: Option<Arc<PreprocResults>>) -> Option<Ops> {
            match lang {
                $(
                    LANG::$camel => {
                        let parser = $parser::new(source, &path, pr);
                        operands_and_operators(&parser, &path)
                    },
                )*
            }
        }
    };
}

macro_rules! mk_extensions {
    ( $( ($camel:ident, [ $( $ext:ident ),* ]) ),* ) => {
        /// Detects the language associated to the input file extension.
        ///
        /// # Examples
        ///
        /// ```
        /// use rust_code_analysis::get_from_ext;
        ///
        /// let ext = "rs";
        ///
        /// get_from_ext(ext).unwrap();
        /// ```
        pub fn get_from_ext(ext: &str) -> Option<LANG>{
            match ext {
                $(
                    $(
                        stringify!($ext) => Some(LANG::$camel),
                    )*
                )*
                _ => None,
            }
        }
    };
}

macro_rules! mk_emacs_mode {
    ( $( ($camel:ident, [ $( $emacs_mode:expr ),* ]) ),* ) => {
        /// Detects the language associated to the input `Emacs` mode.
        ///
        /// An `Emacs` mode is used to detect a language according to
        /// particular text-information contained in a file.
        ///
        /// # Examples
        ///
        /// ```
        /// use rust_code_analysis::get_from_emacs_mode;
        ///
        /// let emacs_mode = "rust";
        ///
        /// get_from_emacs_mode(emacs_mode).unwrap();
        /// ```
        pub fn get_from_emacs_mode(mode: &str) -> Option<LANG>{
            match mode {
                $(
                    $(
                        $emacs_mode => Some(LANG::$camel),
                    )*
                )*
                _ => None,
            }
        }
    };
}

macro_rules! mk_code {
    ( $( ($camel:ident, $code:ident, $parser:ident, $name:ident, $docname:expr) ),* ) => {
        $(
            pub struct $code { _guard: (), }
            impl CodeMetricsT for $code { }

            impl TSLanguage for $code {
                type BaseLang = $camel;

                fn get_lang() -> LANG {
                    LANG::$camel
                }

                fn get_language() -> Language {
                    get_language!($name)
                }

                fn get_lang_name() -> &'static str {
                    stringify!($camel)
                }
            }

            #[doc = "The `"]
            #[doc = $docname]
            #[doc = "` language parser."]
            pub type $parser = Parser<$code>;
        )*
    };
}

macro_rules! mk_langs {
    ( $( ($camel:ident, $description: expr, $display: expr, $code:ident, $parser:ident, $name:ident, [ $( $ext:ident ),* ], [ $( $emacs_mode:expr ),* ]) ),* ) => {
        mk_enum!($( $camel, $description ),*);
        mk_impl_lang!($( ($camel, $name, $display) ),*);
        mk_action!($( ($camel, $parser) ),*);
        mk_extensions!($( ($camel, [ $( $ext ),* ]) ),*);
        mk_emacs_mode!($( ($camel, [ $( $emacs_mode ),* ]) ),*);
        mk_code!($( ($camel, $code, $parser, $name, stringify!($camel)) ),*);
    };
}

macro_rules! color {
    ( $stdout: ident, $color: ident) => {
        $stdout.set_color(ColorSpec::new().set_fg(Some(Color::$color)))?;
    };
    ( $stdout: ident, $color: ident, $intense: ident) => {
        $stdout.set_color(
            ColorSpec::new()
                .set_fg(Some(Color::$color))
                .set_intense($intense),
        )?;
    };
}

#[cfg(test)]
macro_rules! check_metrics {
    ($source: expr, $file: expr, $parser: ident, $metric: ident,
     [ $( ( $func_int: ident, $true_int_value: expr $(,$type_int: ty)? )$(,)* )* ]$(,)*
     $( [ $( ( $func_float: ident, $true_float_value: expr )$(,)* )* ] )?) => {
        {
            let path = PathBuf::from($file);
            let mut trimmed_bytes = $source.trim_end().trim_matches('\n').as_bytes().to_vec();
            trimmed_bytes.push(b'\n');
            let parser = $parser::new(trimmed_bytes, &path, None);
            let func_space = metrics(&parser, &path).unwrap();

            $( assert_eq!(func_space.metrics.$metric.$func_int() $(as $type_int)?, $true_int_value); )*

            $(
                $(
                    assert!(if ($true_float_value as f64).is_nan() {
                        func_space.metrics.$metric.$func_float().is_nan()
                    } else {
                        func_space.metrics.$metric.$func_float().total_cmp(&$true_float_value) == std::cmp::Ordering::Equal
                    });
                )*
            )?
        }
    };
}