Skip to main content

iso10383_parser/
lib.rs

1//! ISO 10383 Market Identifier Codes XML Parser.
2
3#![cfg_attr(doc, doc = include_str!("../README.md"))]
4
5use core::{
6    cell::RefCell,
7    sync::atomic::{AtomicBool, Ordering},
8};
9use iso3166_static::Alpha2;
10use iso10383_types::{Category, Kind, Mic, Status};
11use serde::{Deserialize, Serialize};
12use std::collections::HashMap;
13
14/// A list of MICs which can be parsed from the distributed XML file.
15#[derive(Debug, Default, Deserialize, Serialize)]
16pub struct MicList {
17    /// The list of parsed records.
18    #[serde(alias = "ISO10383_MIC")]
19    mics: Vec<MicRecord>,
20    /// An index of records by code string.
21    #[serde(skip)]
22    by_mics: RefCell<HashMap<String, MicRecord>>,
23    /// Whether the index has been populated.
24    #[serde(skip)]
25    by_mics_loaded: AtomicBool,
26}
27
28impl MicList {
29    /// Populate the index from the initialized data.
30    fn update_cache(&self) {
31        if self.by_mics_loaded.load(Ordering::Acquire) {
32            return;
33        }
34
35        for mic in &self.mics {
36            self.by_mics
37                .borrow_mut()
38                .insert(mic.name.clone(), mic.clone());
39        }
40        self.by_mics_loaded.store(true, Ordering::Release);
41    }
42
43    /// Get the size of the cache.
44    #[inline]
45    pub fn len(&self) -> usize {
46        self.update_cache();
47
48        self.mics.len()
49    }
50
51    /// Get whether or not the cache is empty.
52    #[inline]
53    pub fn is_empty(&self) -> bool {
54        self.update_cache();
55
56        self.mics.is_empty()
57    }
58
59    /// Retrieve a slice of the parsed MICs.
60    #[inline]
61    pub fn mics(&self) -> &[MicRecord] {
62        &self.mics
63    }
64}
65
66/// A structure representing a Market Identifier record.
67#[derive(Clone, Debug, Deserialize, Eq, Hash, Ord, PartialEq, PartialOrd, Serialize)]
68pub struct MicRecord {
69    /// The MIC itself.
70    #[serde(alias = "MIC")]
71    pub mic: Mic,
72
73    /// The "owning/operating" MIC which controls this entry.
74    #[serde(alias = "OPERATING_x0020_MIC")]
75    pub operating_mic: Mic,
76
77    /// What type of MIC this is.
78    #[serde(alias = "OPRT_x002F_SGMT")]
79    pub kind: Kind,
80
81    /// The human-readable name of this MIC.
82    #[serde(alias = "MARKET_x0020_NAME-INSTITUTION_x0020_DESCRIPTION")]
83    pub name: String,
84
85    /// The name of the legal entity responsible for this MIC.
86    #[serde(alias = "LEGAL_x0020_ENTITY_x0020_NAME")]
87    pub legal_entity_name: Option<String>,
88
89    /// The ISO 17442 LEI code for the legal entity.
90    ///
91    /// This is a string because quick-xml treats `<LEI></LEI>` as `Some("")` and tries to parse
92    /// the `""`. A simple `deserialize_with` that returns an `Option<Lei>` also did not work...
93    #[serde(alias = "LEI")]
94    pub legal_entity_id: Option<String>,
95
96    /// The market category this MIC is operating in.
97    #[serde(alias = "MARKET_x0020_CATEGORY_x0020_CODE")]
98    pub category: Category,
99
100    /// Known acronym of the market.
101    #[serde(alias = "ACRONYM")]
102    pub acronym: Option<String>,
103
104    /// ISO 3166-2 alpha-2 code.
105    #[serde(alias = "ISO_x0020_COUNTRY_x0020_CODE_x0020__x0028_ISO_x0020_3166_x0029_")]
106    pub country: Alpha2,
107
108    /// The city where this market is located.
109    #[serde(alias = "CITY")]
110    pub city: String,
111
112    /// The website of this market.
113    #[serde(alias = "WEBSITE")]
114    pub website: Option<String>,
115
116    /// The current status of this code.
117    #[serde(alias = "STATUS")]
118    pub status: Status,
119
120    /// The date this code was originally created in the ASCII decimal format YYYYMMDD.
121    #[serde(alias = "CREATION_x0020_DATE")]
122    pub creation_date: String,
123
124    /// The last update date in the ASCII decimal format YYYYMMDD.
125    #[serde(alias = "LAST_x0020_UPDATE_x0020_DATE")]
126    pub last_update_date: String,
127
128    /// The date this MIC was last verified for correctness in the ASCII decimal format YYYYMMDD.
129    #[serde(alias = "LAST_x0020_VALIDATION_x0020_DATE")]
130    pub last_validation_date: Option<String>,
131
132    /// The date when this MIC was marked inactive in the ASCII decimal format YYYYMMDD.
133    #[serde(alias = "EXPIRY_x0020_DATE")]
134    pub expiry_date: Option<String>,
135
136    /// Additional details or comments.
137    #[serde(alias = "COMMENTS")]
138    pub comments: Option<String>,
139}