/*!
![names](https://img.shields.io/badge/RDFtk-names-BD1B89?logo=data:image/png;base64,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)
This crate provides a set of modules that contain the `Iri`s and `QName` strings for commonly used vocabularies. It also provides macro support for defining new namespaces in the same style as this library.

The vocabularies supported can be found [below](#modules).

# Macro Example

The following example replicates the `geo` module using the `namespace!` macro. Note that as this
macro uses `paste::item` the client will need to have a dependency on the
[paste crate](https://crates.io/crates/paste).


```rust
use rdftk_names::namespace;

namespace! {
    "geo",
    "http://www.w3.org/2003/01/geo/wgs84_pos#",
    {
        spatial_thing, "SpatialThing",
        temporal_thing, "TemporalThing",
        event, "Event",
        point, "Point",
        lat, "lat",
        location, "location",
        long, "long",
        alt, "alt",
        lat_long, "lat_long"
    }
}
```

*/

#![warn(
    // ---------- Stylistic
    future_incompatible,
    nonstandard_style,
    rust_2018_idioms,
    trivial_casts,
    trivial_numeric_casts,
    // ---------- Public
    missing_debug_implementations,
    missing_docs,
    unreachable_pub,
    // ---------- Unsafe
    unsafe_code,
    // ---------- Unused
    unused_extern_crates,
    unused_import_braces,
    unused_qualifications,
    unused_results,
)]

// ------------------------------------------------------------------------------------------------
// Macros
// ------------------------------------------------------------------------------------------------

///
/// This macro produces the constants and functions to describe a vocabulary.
///
/// # Example
///
/// Given the following namespace invocation,
///
/// ```rust
/// use rdftk_names::namespace;
///
/// namespace!(
///     "fb",
///     "http://example.com/schema/FooBar#",
///     {
///         foo, "Foo"
///     }
/// );
/// ```
///
/// The following would be generated.
///
/// ```rust
/// /// Returns the commonly used prefix string for this namespace.
/// pub fn default_prefix() -> &'static ::rdftk_iri::Name { todo!() }
///
/// ///  Returns the Iri identifying this namespace.
/// pub fn namespace() -> &'static ::rdftk_iri::Iri { todo!() }
///
/// /// Returns the Iri, as a string, identifying this namespace.
/// pub fn namespace_str() -> &'static str { todo!() }
///
/// // ***** For each namespace member: *****
///
/// /// Returns the qualified Iri for the namespace member `Foo`.
/// pub fn foo() -> &'static ::rdftk_iri::Iri { todo!() }
///
/// /// Returns this member's name, as the string "Foo".
/// pub fn foo_str() -> &'static str { todo!() }
///
/// /// Returns a qualified name, using the default prefix, for the namespace  member `Foo`.
/// pub fn foo_qname() -> &'static str { todo!() }
/// ```
///
#[macro_export]
macro_rules! namespace {
    ($prefix:expr, $namespace:expr, { $($fn_name:ident, $name:expr),* }) => {

        #[doc(hidden)]
        const PREFIX: &str = $prefix;

        #[doc(hidden)]
        const NAMESPACE: &str = $namespace;

        ::lazy_static::lazy_static! {
            #[doc(hidden)]
            static ref NS_IRI: ::rdftk_iri::Iri =
                <::rdftk_iri::Iri as ::std::str::FromStr>::from_str(NAMESPACE).unwrap();

            #[doc(hidden)]
            static ref NS_PREFIX: ::rdftk_iri::Name =
                <::rdftk_iri::Name as ::std::str::FromStr>::from_str(PREFIX).unwrap();

            #[doc(hidden)]
            static ref NS_CACHE: ::std::collections::HashMap<String, (::rdftk_iri::Iri, &'static str)>
                = make_cache();
        }

        #[doc(hidden)]
        fn make_cache() -> ::std::collections::HashMap<String, (::rdftk_iri::Iri, &'static str)> {
            let mut cache: ::std::collections::HashMap<String, (::rdftk_iri::Iri, &'static str)>
                = Default::default();
            $(
                let _ = cache.insert(
                    $name.to_string(),
                    (
                        <::rdftk_iri::Iri as ::rdftk_iri::IriExtra>::make_name(
                                &NS_IRI,
                                <::rdftk_iri::Name as ::std::str::FromStr>::from_str($name).unwrap()
                            ).unwrap(),
                        concat!($prefix, ":", $name),
                    )
                );
            )*
            cache
        }

        #[inline(always)]
        #[doc = "Returns the commonly used prefix string for this namespace."]
        pub fn default_prefix() -> &'static ::rdftk_iri::Name { &NS_PREFIX }

        #[inline(always)]
        #[doc = "Returns the IRI, as a string, identifying this namespace."]
        pub fn namespace_str() -> &'static str { NAMESPACE }

        #[inline(always)]
        #[doc = "Returns the IRI identifying this namespace."]
        pub fn namespace() -> &'static ::rdftk_iri::Iri { &NS_IRI }

        $(
            $crate::nsname!($fn_name, $name);
        )*
    };
}

///
/// This macro *should only* called by the `namespace!` macro. It takes an identifier and a
/// string and produces:
///
/// 1. a function with the same identifier which returns a complete Iri using the
///    value of `NAMESPACE` in the current scope, and
/// 1. a function with the same identifier, but the suffix `_str` which returns the name
///    as a string.
/// 1. a function with the same identifier, but the suffix `_qname` which returns a qualified name
///    using the value of `PREFIX` in the current scope.
///
#[macro_export]
macro_rules! nsname {
    ($fn_name:ident, _) => {
        nsname!{ $fn_name, stringify!($fn_name) }
    };
    ($fn_name:ident, $name:expr) => {
        ::paste::paste! {
            #[inline(always)]
            #[doc = "Returns the qualified IRI for the namespace member `" $name "`."]
            pub fn $fn_name() -> &'static ::rdftk_iri::Iri {
                &NS_CACHE.get($name).unwrap().0
            }

            #[inline(always)]
            #[doc = "Returns this member's name, as the string \"" $name "\"."]
            pub fn [<$fn_name _str>]() -> &'static str {
                $name
            }

            #[inline(always)]
            #[doc = "Returns a qualified name, using the default prefix, for the namespace  member `" $name "`."]
            pub fn [<$fn_name _qname>]() -> &'static str {
                &NS_CACHE.get($name).unwrap().1
            }
         }
    };
}

// ------------------------------------------------------------------------------------------------
// Modules
// ------------------------------------------------------------------------------------------------

pub mod dc;

pub mod foaf;

pub mod geo;

pub mod owl;

pub mod rdf;

pub mod rdfs;

pub mod skos;

pub mod xsd;

// ------------------------------------------------------------------------------------------------
// Unit Tests
// ------------------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    #![allow(unreachable_pub)]

    use super::*;
    use rdftk_iri::Iri;
    use std::str::FromStr;

    namespace!("p", "heep://schema/com/p#", { foo, "Foo", bar, "Bar" } );

    #[test]
    fn test_expand_default_prefix() {
        assert_eq!(default_prefix().as_ref(), "p".to_string());
    }

    #[test]
    fn test_expand_namespace_str() {
        assert_eq!(namespace_str(), "heep://schema/com/p#");
    }

    #[test]
    fn test_expand_namespace_iri() {
        assert_eq!(namespace(), &Iri::from_str("heep://schema/com/p#").unwrap());
    }

    #[test]
    fn test_expand_member_foo() {
        assert_eq!(foo().to_string(), "heep://schema/com/p#Foo");
        assert_eq!(foo_str(), "Foo");
        assert_eq!(foo_qname(), "p:Foo");
    }

    #[test]
    fn test_expand_member_bar() {
        assert_eq!(bar().to_string(), "heep://schema/com/p#Bar");
        assert_eq!(bar_str(), "Bar");
        assert_eq!(bar_qname(), "p:Bar");
    }
}
