codes_agency/
lib.rs

1/*!
2This package provides a common code representing standards agencies.
3
4The two core types, [Agency] and [Standard] work together to provide reporting
5capabilities to other *codes* project packages. Specifically a package that
6provides types corresponding to a standard definition can have an instance of
7the [Standard] struct that describes the standard. This in turn references the
8[Agency] that controls the standard.
9
10```rust
11use codes_agency::{Agency, Standard};
12
13// Taken from codes_iso_4217
14pub const ISO_4217: Standard = Standard::new_with_long_ref(
15    Agency::ISO,
16    "4217",
17    "ISO 4217:2015",
18    "Currency codes",
19    "https://www.iso.org/iso-4217-currency-codes.html",
20);
21
22assert_eq!(ISO_4217.agency().to_string(), String::from("ISO"));
23assert_eq!(ISO_4217.short_ref(), "4217");
24assert_eq!(ISO_4217.long_ref(), Some(&"ISO 4217:2015"));
25assert_eq!(ISO_4217.title(), "Currency codes");
26assert_eq!(ISO_4217.url(), "https://www.iso.org/iso-4217-currency-codes.html");
27```
28*/
29
30#![warn(
31    unknown_lints,
32    // ---------- Stylistic
33    absolute_paths_not_starting_with_crate,
34    elided_lifetimes_in_paths,
35    explicit_outlives_requirements,
36    macro_use_extern_crate,
37    nonstandard_style, /* group */
38    noop_method_call,
39    rust_2018_idioms,
40    single_use_lifetimes,
41    trivial_casts,
42    trivial_numeric_casts,
43    // ---------- Future
44    future_incompatible, /* group */
45    rust_2021_compatibility, /* group */
46    // ---------- Public
47    missing_debug_implementations,
48    // missing_docs,
49    unreachable_pub,
50    // ---------- Unsafe
51    unsafe_code,
52    unsafe_op_in_unsafe_fn,
53    // ---------- Unused
54    unused, /* group */
55)]
56#![deny(
57    // ---------- Public
58    exported_private_dependencies,
59    private_in_public,
60    // ---------- Deprecated
61    anonymous_parameters,
62    bare_trait_objects,
63    ellipsis_inclusive_range_patterns,
64    // ---------- Unsafe
65    deref_nullptr,
66    drop_bounds,
67    dyn_drop,
68)]
69
70use codes_common::code_impl;
71use std::str::FromStr;
72
73#[cfg(feature = "serde")]
74use serde::{Deserialize, Serialize};
75
76// ------------------------------------------------------------------------------------------------
77// Public Types
78// ------------------------------------------------------------------------------------------------
79
80///
81/// This enumeration allows for the identification of well-known standards agencies. This is
82/// useful in documenting crates that implement such standards.
83///
84#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
85#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
86pub enum Agency {
87    /// [GS1](https://www.gs1.org)
88    GS1,
89    /// [Internet Assigned Numbers Authority](https://www.iana.org)
90    IANA,
91    /// [IEEE](https://www.ieee.org)
92    IEEE,
93    /// [The Internet Engineering Task Force](https://www.ietf.org)
94    IETF,
95    /// [International Organization for Standardization](https://www.iso.org)
96    ISO,
97    /// [The United Nations](https://www.un.org)
98    UN,
99}
100
101/// Provides an array of all defined [Agency] codes, useful for queries.
102pub const ALL_CODES: [Agency; 4] = [Agency::IANA, Agency::IEEE, Agency::IETF, Agency::ISO];
103
104///
105/// This structure allows for the description of a specific standard, or specification,
106/// issued by a well-known standards agency. Note that different versions of a standard
107/// should be different instances with *at least* different long references.
108///
109#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
110#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
111pub struct Standard {
112    agency: Agency,
113    short_ref: &'static str,
114    long_ref: Option<&'static str>,
115    title: &'static str,
116    url: &'static str,
117}
118
119pub trait Standardized {
120    fn defining_standard() -> &'static Standard;
121}
122
123///
124/// An error associated with handling String representations of the Agency short name.
125///
126#[derive(Clone, Debug, PartialEq, Eq)]
127#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
128pub enum AgencyError {
129    /// An error generated by `FromStr`
130    FromStr(String),
131}
132
133// ------------------------------------------------------------------------------------------------
134// Public Macros
135// ------------------------------------------------------------------------------------------------
136
137#[macro_export]
138macro_rules! standardized_type {
139    ($type_name:ty, $standard:expr) => {
140        impl $crate::Standardized for $type_name {
141            fn defining_standard() -> &'static $crate::Standard {
142                &$standard
143            }
144        }
145    };
146}
147// ------------------------------------------------------------------------------------------------
148// Implementations
149// ------------------------------------------------------------------------------------------------
150
151impl FromStr for Agency {
152    type Err = AgencyError;
153
154    fn from_str(s: &str) -> Result<Self, Self::Err> {
155        match s {
156            "GS1" => Ok(Self::GS1),
157            "IANA" => Ok(Self::IANA),
158            "IEEE" => Ok(Self::IEEE),
159            "IETF" => Ok(Self::IETF),
160            "ISO" => Ok(Self::ISO),
161            "UN" => Ok(Self::UN),
162            _ => Err(AgencyError::FromStr(s.to_string())),
163        }
164    }
165}
166
167code_impl!(Agency, short_name);
168
169impl Agency {
170    ///
171    /// Return the short name, usually an acronym or abbreviation, of the agency.
172    /// This is usually the same set of characters as the variant name.
173    ///
174    pub const fn short_name(&self) -> &'static str {
175        match self {
176            Self::GS1 => "GS1",
177            Self::IANA => "IANA",
178            Self::IEEE => "IEEE",
179            Self::IETF => "IETF",
180            Self::ISO => "ISO",
181            Self::UN => "UN",
182        }
183    }
184
185    ///
186    /// A longer name, if one exists, for the agency.
187    ///
188    pub const fn name(&self) -> &'static str {
189        match self {
190            Self::GS1 => "GS1 AISBL",
191            Self::IANA => "Internet Assigned Numbers Authority",
192            Self::IEEE => "IEEE",
193            Self::IETF => "The Internet Engineering Task Force",
194            Self::ISO => "International Organization for Standardization",
195            Self::UN => "The United Nations",
196        }
197    }
198
199    ///
200    /// A URL for the agency.
201    ///
202    pub const fn url(&self) -> &'static str {
203        match self {
204            Self::GS1 => "https://www.gs1.org",
205            Self::IANA => "https://www.iana.org",
206            Self::IEEE => "https://www.ieee.org",
207            Self::IETF => "https://www.ietf.org",
208            Self::ISO => "https://www.iso.org",
209            Self::UN => "https://www.un.org",
210        }
211    }
212
213    ///
214    /// Some agencies are hierarchical, this returns a parent agency, if one exists.
215    ///
216    pub const fn parent_agency(&self) -> Option<&Self> {
217        None
218    }
219
220    ///
221    /// Return a [URN](https://www.rfc-editor.org/rfc/rfc8141) that identifies
222    /// an agency. The *namespace identifier* is "agency" (this is not a
223    /// [registered](https://www.iana.org/assignments/urn-namespaces/urn-namespaces.xhtml)
224    /// value) and the *namespace-specific string* is the agency and parent
225    /// agency set in order and separated with "/".
226    ///
227    /// # Example
228    ///
229    /// ```rust,ignore
230    /// let urn = Agency::CEFACT.agency_urn();
231    /// assert_eq!(&urn, "urn:agency:un/unece/cefact");
232    /// ```
233    ///
234    pub fn agency_urn(&self) -> String {
235        let mut path = vec![self.short_name().to_lowercase()];
236        let agency = self;
237        while let Some(agency) = agency.parent_agency() {
238            path.insert(0, agency.short_name().to_lowercase());
239        }
240        format!("urn:agency:{}", path.join("/"))
241    }
242}
243
244// ------------------------------------------------------------------------------------------------
245
246impl Standard {
247    ///
248    /// Create a new Standard **without** a long reference value.
249    ///
250    pub const fn new(
251        agency: Agency,
252        short_ref: &'static str,
253        title: &'static str,
254        url: &'static str,
255    ) -> Self {
256        Self {
257            agency,
258            short_ref,
259            long_ref: None,
260            title,
261            url,
262        }
263    }
264
265    ///
266    /// Create a new Standard **with** a long reference value.
267    ///
268    pub const fn new_with_long_ref(
269        agency: Agency,
270        short_ref: &'static str,
271        long_ref: &'static str,
272        title: &'static str,
273        url: &'static str,
274    ) -> Self {
275        Self {
276            agency,
277            short_ref,
278            long_ref: Some(long_ref),
279            title,
280            url,
281        }
282    }
283
284    ///
285    /// Return the [Agency] that controls this standard.
286    ///
287    pub const fn agency(&self) -> Agency {
288        self.agency
289    }
290
291    ///
292    /// Return the short reference, or number, of this standard.
293    ///
294    pub const fn short_ref(&self) -> &'static str {
295        self.short_ref
296    }
297
298    ///
299    /// Return a longer reference, if one exists, for this standard,
300    ///
301    pub const fn long_ref(&self) -> Option<&&'static str> {
302        self.long_ref.as_ref()
303    }
304
305    ///
306    /// Return the textual title of this standard.
307    ///
308    pub const fn title(&self) -> &'static str {
309        self.title
310    }
311
312    ///
313    /// Return the URL for this standard.
314    ///
315    pub const fn url(&self) -> &'static str {
316        self.url
317    }
318}
319
320// ------------------------------------------------------------------------------------------------
321
322impl std::fmt::Display for AgencyError {
323    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
324        write!(
325            f,
326            "{}",
327            match self {
328                Self::FromStr(s) => format!("The short name {:?} is not a known agency", s),
329            }
330        )
331    }
332}
333
334impl std::error::Error for AgencyError {}
335
336// ------------------------------------------------------------------------------------------------
337// Modules
338// ------------------------------------------------------------------------------------------------