codes-agency 0.1.8

This package provides a common code representing standards agencies
Documentation
/*!
This package provides a common code representing standards agencies.

The two core types, [Agency] and [Standard] work together to provide reporting
capabilities to other *codes* project packages. Specifically a package that
provides types corresponding to a standard definition can have an instance of
the [Standard] struct that describes the standard. This in turn references the
[Agency] that controls the standard.

```rust
use codes_agency::{Agency, Standard};

// Taken from codes_iso_4217
pub const ISO_4217: Standard = Standard::new_with_long_ref(
    Agency::ISO,
    "4217",
    "ISO 4217:2015",
    "Currency codes",
    "https://www.iso.org/iso-4217-currency-codes.html",
);

assert_eq!(ISO_4217.agency().to_string(), String::from("ISO"));
assert_eq!(ISO_4217.short_ref(), "4217");
assert_eq!(ISO_4217.long_ref(), Some(&"ISO 4217:2015"));
assert_eq!(ISO_4217.title(), "Currency codes");
assert_eq!(ISO_4217.url(), "https://www.iso.org/iso-4217-currency-codes.html");
```
*/

#![warn(
    unknown_lints,
    // ---------- Stylistic
    absolute_paths_not_starting_with_crate,
    elided_lifetimes_in_paths,
    explicit_outlives_requirements,
    macro_use_extern_crate,
    nonstandard_style, /* group */
    noop_method_call,
    rust_2018_idioms,
    single_use_lifetimes,
    trivial_casts,
    trivial_numeric_casts,
    // ---------- Future
    future_incompatible, /* group */
    rust_2021_compatibility, /* group */
    // ---------- Public
    missing_debug_implementations,
    // missing_docs,
    unreachable_pub,
    // ---------- Unsafe
    unsafe_code,
    unsafe_op_in_unsafe_fn,
    // ---------- Unused
    unused, /* group */
)]
#![deny(
    // ---------- Public
    exported_private_dependencies,
    private_in_public,
    // ---------- Deprecated
    anonymous_parameters,
    bare_trait_objects,
    ellipsis_inclusive_range_patterns,
    // ---------- Unsafe
    deref_nullptr,
    drop_bounds,
    dyn_drop,
)]

use codes_common::code_impl;
use std::str::FromStr;

#[cfg(feature = "serde")]
use serde::{Deserialize, Serialize};

// ------------------------------------------------------------------------------------------------
// Public Types
// ------------------------------------------------------------------------------------------------

///
/// This enumeration allows for the identification of well-known standards agencies. This is
/// useful in documenting crates that implement such standards.
///
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum Agency {
    /// [GS1](https://www.gs1.org)
    GS1,
    /// [Internet Assigned Numbers Authority](https://www.iana.org)
    IANA,
    /// [IEEE](https://www.ieee.org)
    IEEE,
    /// [The Internet Engineering Task Force](https://www.ietf.org)
    IETF,
    /// [International Organization for Standardization](https://www.iso.org)
    ISO,
    /// [The United Nations](https://www.un.org)
    UN,
}

/// Provides an array of all defined [Agency] codes, useful for queries.
pub const ALL_CODES: [Agency; 4] = [Agency::IANA, Agency::IEEE, Agency::IETF, Agency::ISO];

///
/// This structure allows for the description of a specific standard, or specification,
/// issued by a well-known standards agency. Note that different versions of a standard
/// should be different instances with *at least* different long references.
///
#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub struct Standard {
    agency: Agency,
    short_ref: &'static str,
    long_ref: Option<&'static str>,
    title: &'static str,
    url: &'static str,
}

pub trait Standardized {
    fn defining_standard() -> &'static Standard;
}

///
/// An error associated with handling String representations of the Agency short name.
///
#[derive(Clone, Debug, PartialEq, Eq)]
#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
pub enum AgencyError {
    /// An error generated by `FromStr`
    FromStr(String),
}

// ------------------------------------------------------------------------------------------------
// Public Functions
// ------------------------------------------------------------------------------------------------

// ------------------------------------------------------------------------------------------------
// Implementations
// ------------------------------------------------------------------------------------------------

impl FromStr for Agency {
    type Err = AgencyError;

    fn from_str(s: &str) -> Result<Self, Self::Err> {
        match s {
            "GS1" => Ok(Self::GS1),
            "IANA" => Ok(Self::IANA),
            "IEEE" => Ok(Self::IEEE),
            "IETF" => Ok(Self::IETF),
            "ISO" => Ok(Self::ISO),
            "UN" => Ok(Self::UN),
            _ => Err(AgencyError::FromStr(s.to_string())),
        }
    }
}

code_impl!(Agency, short_name);

impl Agency {
    ///
    /// Return the short name, usually an acronym or abbreviation, of the agency.
    /// This is usually the same set of characters as the variant name.
    ///
    pub const fn short_name(&self) -> &'static str {
        match self {
            Self::GS1 => "GS1",
            Self::IANA => "IANA",
            Self::IEEE => "IEEE",
            Self::IETF => "IETF",
            Self::ISO => "ISO",
            Self::UN => "UN",
        }
    }

    ///
    /// A longer name, if one exists, for the agency.
    ///
    pub const fn name(&self) -> &'static str {
        match self {
            Self::GS1 => "GS1 AISBL",
            Self::IANA => "Internet Assigned Numbers Authority",
            Self::IEEE => "IEEE",
            Self::IETF => "The Internet Engineering Task Force",
            Self::ISO => "International Organization for Standardization",
            Self::UN => "The United Nations",
        }
    }

    ///
    /// A URL for the agency.
    ///
    pub const fn url(&self) -> &'static str {
        match self {
            Self::GS1 => "https://www.gs1.org",
            Self::IANA => "https://www.iana.org",
            Self::IEEE => "https://www.ieee.org",
            Self::IETF => "https://www.ietf.org",
            Self::ISO => "https://www.iso.org",
            Self::UN => "https://www.un.org",
        }
    }

    ///
    /// Some agencies are hierarchical, this returns a parent agency, if one exists.
    ///
    pub const fn parent_agency(&self) -> Option<&Self> {
        None
    }

    ///
    /// Return a [URN](https://www.rfc-editor.org/rfc/rfc8141) that identifies
    /// an agency. The *namespace identifier* is "agency" (this is not a
    /// [registered](https://www.iana.org/assignments/urn-namespaces/urn-namespaces.xhtml)
    /// value) and the *namespace-specific string* is the agency and parent
    /// agency set in order and separated with "/".
    ///
    /// # Example
    ///
    /// ```rust,ignore
    /// let urn = Agency::CEFACT.agency_urn();
    /// assert_eq!(&urn, "urn:agency:un/unece/cefact");
    /// ```
    ///
    pub fn agency_urn(&self) -> String {
        let mut path = vec![self.short_name().to_lowercase()];
        let agency = self;
        while let Some(agency) = agency.parent_agency() {
            path.insert(0, agency.short_name().to_lowercase());
        }
        format!("urn:agency:{}", path.join("/"))
    }
}

// ------------------------------------------------------------------------------------------------

impl Standard {
    ///
    /// Create a new Standard **without** a long reference value.
    ///
    pub const fn new(
        agency: Agency,
        short_ref: &'static str,
        title: &'static str,
        url: &'static str,
    ) -> Self {
        Self {
            agency,
            short_ref,
            long_ref: None,
            title,
            url,
        }
    }

    ///
    /// Create a new Standard **with** a long reference value.
    ///
    pub const fn new_with_long_ref(
        agency: Agency,
        short_ref: &'static str,
        long_ref: &'static str,
        title: &'static str,
        url: &'static str,
    ) -> Self {
        Self {
            agency,
            short_ref,
            long_ref: Some(long_ref),
            title,
            url,
        }
    }

    ///
    /// Return the [Agency] that controls this standard.
    ///
    pub const fn agency(&self) -> Agency {
        self.agency
    }

    ///
    /// Return the short reference, or number, of this standard.
    ///
    pub const fn short_ref(&self) -> &'static str {
        self.short_ref
    }

    ///
    /// Return a longer reference, if one exists, for this standard,
    ///
    pub const fn long_ref(&self) -> Option<&&'static str> {
        self.long_ref.as_ref()
    }

    ///
    /// Return the textual title of this standard.
    ///
    pub const fn title(&self) -> &'static str {
        self.title
    }

    ///
    /// Return the URL for this standard.
    ///
    pub const fn url(&self) -> &'static str {
        self.url
    }
}

// ------------------------------------------------------------------------------------------------

impl std::fmt::Display for AgencyError {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(
            f,
            "{}",
            match self {
                Self::FromStr(s) => format!("The short name {:?} is not a known agency", s),
            }
        )
    }
}

impl std::error::Error for AgencyError {}

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