code-docs-rs 0.2.0

Use code docs as runtime documentation
Documentation
#[derive(Clone, Copy)]
pub struct DocumentedConstants {
    names: &'static [&'static str],
    types: &'static [&'static str],
    values: &'static [&'static str],
    docs: &'static [&'static [&'static str]],
}

impl DocumentedConstants {
    /// Returns all constants names
    pub fn constant_names(&self) -> &[&'static str] {
        self.names
    }

    /// Returns all constants types
    pub fn constant_types(&self) -> &[&'static str] {
        self.types
    }

    /// Returns all constants values (raw string)
    pub fn constant_values(&self) -> &[&'static str] {
        self.values
    }

    pub fn constant_docs_raw(&self) -> &[&[&'static str]] {
        self.docs
    }

    /// Returns all constants docstrings, one line being one string
    pub fn constant_docs(&self) -> Vec<Vec<&'static str>> {
        self.constant_docs_raw()
            .iter().map(|x| {
            x.into_iter()
                .filter_map(|x| super::filter_docs(*x))
                .collect::<Vec<_>>()
        })
        .collect::<Vec<_>>()
    }

    /// Returns string where each constant is shown with its type and docstring
    pub fn commented_constants(&self) -> String {
        use std::fmt::Write;

        // NOTE write! is infallable on String

        let mut output = String::new();
        // just in case so the errors are not so cryptic below
        assert_eq!(self.names.len(), self.types.len(), "Constant names and types length are not equal");
        assert_eq!(self.names.len(), self.values.len(), "Constant names and values length are not equal");
        assert_eq!(self.names.len(), self.docs.len(), "Constant names and docs length are not equal");

        // NOTE im not showing value as it is usually ugly not understandable without processing
        for (i, field) in self.names.iter().enumerate() {
            // remove the extra newline
            if i != 0 {
                write!(output, "\n").unwrap();
            }

            // imitate rust comments
            for comment in self.docs.get(i).unwrap().iter() {
                if let Some(comment) = super::filter_docs(comment) {
                    write!(output, "///{}\n", comment).unwrap();
                }
            }

            write!(output, "{}: {}\n", field, self.types.get(i).unwrap()).unwrap();
        }

        output
    }
}

/// Saves information about constants defined within in a constant
#[macro_export]
macro_rules! code_docs_constants {
    (
        $t_vis:vis $t_name:ident;

        $(
            $(#[$meta:meta])*
            $vis:vis const $name:ident : $t:ty = $expr:expr ;
        )*
    ) => {
        $t_vis const $t_name: $crate::DocumentedConstants = $crate::DocumentedConstants {
            names: &[
                $(
                    stringify!($name),
                )*
            ],
            types: &[
                $(
                    stringify!($t),
                )*
            ],
            values: &[
                $(
                    stringify!($expr),
                )*
            ],
            docs: &[
                $(
                    &[
                        $(stringify!($meta),)*
                    ],
                )*
            ],
        };

        $(
            $(#[$meta])*
            $vis const $name : $t = $expr ;
        )*
    }
}

#[cfg(test)]
#[allow(unused)]
mod tests {
    code_docs_constants! {
        pub DOCS;

        const ENV_FEATURE1: &str = "something";

        /// Does the thing with feature2
        ///
        /// Yes very important
        #[allow(unused)]
        const ENV_FEATURE2: usize = 666;
    }

    #[test]
    fn test_constants() {
        assert_eq!(DOCS.constant_names(), &["ENV_FEATURE1", "ENV_FEATURE2"]);
        assert_eq!(DOCS.constant_types(), &["&str", "usize"]);
        assert_eq!(DOCS.constant_values(), &["\"something\"", "666"]);

        assert_eq!(DOCS.constant_docs_raw(), vec![
            vec![],
            vec![
                "doc = r\" Does the thing with feature2\"",
                "doc = r\"\"",
                "doc = r\" Yes very important\"",
                "allow(unused)"
            ],
        ]);

        assert_eq!(DOCS.constant_docs(), vec![
            vec![],
            vec![
            " Does the thing with feature2",
            "",
            " Yes very important",
        ]]);
    }
}