Skip to main content

iso10383_types/
lib.rs

1//! ISO 10383 Types
2
3#![doc = include_str!("../README.md")]
4#![no_std]
5
6#[cfg(feature = "alloc")]
7extern crate alloc;
8
9#[cfg(feature = "alloc")]
10mod alloc_;
11
12#[cfg(feature = "serde")]
13mod serde_;
14
15use core::{
16    borrow::Borrow,
17    fmt::{Display, Error as FmtError, Formatter, Result as FmtResult},
18    ops::Deref,
19    str::FromStr,
20};
21use ref_cast::{RefCastCustom, ref_cast_custom};
22use thiserror::Error as ThisError;
23
24#[cfg(feature = "serde")]
25use ::serde::{Deserialize, Serialize};
26
27const MIC_SIZE: usize = 4;
28
29const fn check_mic(bytes: &[u8]) -> Result<(), Error> {
30    if bytes.len() != MIC_SIZE {
31        return Err(Error::InvalidLength(bytes.len(), MIC_SIZE));
32    }
33
34    let mut i = 0;
35    while i < MIC_SIZE {
36        if !bytes[i].is_ascii_digit() && !bytes[i].is_ascii_uppercase() {
37            return Err(Error::InvalidCharacter(i));
38        }
39
40        i += 1;
41    }
42
43    Ok(())
44}
45
46/// An enumeration of errors when validating a MIC
47#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, ThisError)]
48#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
49pub enum Error {
50    /// Invalid length.
51    #[error("Invalid length.")]
52    InvalidLength(usize, usize),
53    /// Invalid character at position {0}.
54    #[error("Invalid character at position {0}.")]
55    InvalidCharacter(usize),
56}
57
58/// A MIC reference.
59#[derive(Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
60#[allow(non_camel_case_types)]
61#[repr(transparent)]
62pub struct mic([u8]);
63
64impl mic {
65    #[ref_cast_custom]
66    pub(crate) const fn from_bytes_unchecked(src: &[u8]) -> &Self;
67
68    /// Create a new MIC from the given bytes.
69    pub const fn from_bytes(src: &[u8]) -> Result<&Self, Error> {
70        if let Err(e) = check_mic(src) {
71            Err(e)
72        } else {
73            Ok(Self::from_bytes_unchecked(src))
74        }
75    }
76
77    /// Create a new MIC from the given string.
78    pub const fn from_str(src: &str) -> Result<&Self, Error> {
79        let bytes = src.as_bytes();
80
81        if let Err(e) = check_mic(bytes) {
82            Err(e)
83        } else {
84            Ok(Self::from_bytes_unchecked(bytes))
85        }
86    }
87
88    /// Borrow this MIC as a byte slice.
89    pub const fn as_bytes(&self) -> &[u8] {
90        &self.0
91    }
92
93    /// Borrow this MIC as a string slice.
94    #[allow(unsafe_code)]
95    pub const fn as_str(&self) -> &str {
96        // SAFETY: a mic slice is validated before construction
97        unsafe { str::from_utf8_unchecked(&self.0) }
98    }
99
100    /// Get an owned copy of this MIC.
101    pub const fn to_mic(&self) -> Mic {
102        Mic::from_bytes_unchecked(&self.0)
103    }
104}
105
106impl AsRef<[u8]> for mic {
107    fn as_ref(&self) -> &[u8] {
108        self.as_bytes()
109    }
110}
111
112impl AsRef<str> for mic {
113    fn as_ref(&self) -> &str {
114        self.as_str()
115    }
116}
117
118impl Display for mic {
119    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
120        write!(f, "{}", str::from_utf8(&self.0).map_err(|_e| FmtError)?)
121    }
122}
123
124/// An owned MIC value.
125#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd, RefCastCustom)]
126#[cfg_attr(
127    feature = "zerocopy",
128    derive(zerocopy::Immutable, zerocopy::IntoBytes, zerocopy::KnownLayout)
129)]
130#[repr(transparent)]
131pub struct Mic([u8; MIC_SIZE]);
132
133impl Mic {
134    pub(crate) const fn from_byte_array_unchecked(bytes: [u8; MIC_SIZE]) -> Self {
135        Self(bytes)
136    }
137
138    pub(crate) const fn from_bytes_unchecked(src: &[u8]) -> Self {
139        let mut bytes = [0u8; MIC_SIZE];
140        let (value, _reject) = src.split_at(MIC_SIZE);
141        bytes.copy_from_slice(value);
142        Self::from_byte_array_unchecked(bytes)
143    }
144
145    /// Create a new MIC by taking ownership of a byte array.
146    pub const fn from_byte_array(bytes: [u8; MIC_SIZE]) -> Result<Self, Error> {
147        if let Err(e) = check_mic(&bytes) {
148            Err(e)
149        } else {
150            Ok(Self::from_byte_array_unchecked(bytes))
151        }
152    }
153
154    /// Create a new owned MIC from the given byte slice.
155    pub const fn from_bytes(src: &[u8]) -> Result<Self, Error> {
156        if let Err(e) = check_mic(src) {
157            Err(e)
158        } else {
159            Ok(Self::from_bytes_unchecked(src))
160        }
161    }
162
163    /// Create a new owned MIC from the given string slice.
164    pub const fn from_str_slice(s: &str) -> Result<Self, Error> {
165        Self::from_bytes(s.as_bytes())
166    }
167
168    /// Borrow this MIC as a byte slice.
169    pub const fn as_bytes(&self) -> &[u8] {
170        &self.0
171    }
172
173    /// Borrow this MIC as a string slice.
174    #[allow(unsafe_code)]
175    pub const fn as_str(&self) -> &str {
176        // SAFETY: We validate the internal byte array contains only ASCII digits and uppercase
177        // characters on construction.
178        unsafe { str::from_utf8_unchecked(&self.0) }
179    }
180
181    /// Borrow this MIC as a MIC slice.
182    pub const fn as_mic(&self) -> &mic {
183        mic::from_bytes_unchecked(&self.0)
184    }
185}
186
187impl AsRef<[u8]> for Mic {
188    fn as_ref(&self) -> &[u8] {
189        self.as_bytes()
190    }
191}
192
193impl AsRef<str> for Mic {
194    fn as_ref(&self) -> &str {
195        self.as_str()
196    }
197}
198
199impl Deref for Mic {
200    type Target = mic;
201
202    fn deref(&self) -> &Self::Target {
203        mic::from_bytes_unchecked(&self.0)
204    }
205}
206
207impl Borrow<mic> for Mic {
208    fn borrow(&self) -> &mic {
209        mic::from_bytes_unchecked(&self.0)
210    }
211}
212
213impl Display for Mic {
214    fn fmt(&self, f: &mut Formatter<'_>) -> FmtResult {
215        write!(f, "{}", str::from_utf8(&self.0).map_err(|_e| FmtError)?)
216    }
217}
218
219impl FromStr for Mic {
220    type Err = Error;
221
222    fn from_str(s: &str) -> Result<Self, Self::Err> {
223        Self::from_str_slice(s)
224    }
225}
226
227/// The type of MIC.
228#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
229#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
230pub enum Kind {
231    /// A top-level owner/operator organization
232    #[cfg_attr(feature = "serde", serde(alias = "OPRT"))]
233    Operating,
234    /// A market segment MIC subsidiary of an owner/operator MIC
235    #[cfg_attr(feature = "serde", serde(alias = "SGMT"))]
236    Segment,
237}
238
239/// The market category of a MIC.
240#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
241#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
242pub enum Category {
243    /// ATSS - Alternative Trading System.
244    #[cfg_attr(feature = "serde", serde(alias = "ATSS"))]
245    AlternativeTradingSystem,
246
247    /// APPA - Approved Publication Arrangement.
248    #[cfg_attr(feature = "serde", serde(alias = "APPA"))]
249    ApprovedPublicationArrangement,
250
251    /// ARMS - Approved Reporting Mechanism.
252    #[cfg_attr(feature = "serde", serde(alias = "ARMS"))]
253    ApprovedReportingMechanism,
254
255    /// CTPS - Consolidated Tape Provider.
256    #[cfg_attr(feature = "serde", serde(alias = "CTPS"))]
257    ConsolidatedTapeProvider,
258
259    /// CASP - Crypto Asset Services Provider.
260    #[cfg_attr(feature = "serde", serde(alias = "CASP"))]
261    CryptoAssetServicesProvider,
262
263    /// DCMS - Designated Contract Market.
264    #[cfg_attr(feature = "serde", serde(alias = "DCMS"))]
265    DesignatedContractMarket,
266
267    /// IDQS - Inter Dealer Quotation System.
268    #[cfg_attr(feature = "serde", serde(alias = "IDQS"))]
269    InterDealerQuotationSystem,
270
271    /// MLTF - Multilateral Trading Facility.
272    #[cfg_attr(feature = "serde", serde(alias = "MLTF"))]
273    MultilateralTradingFacility,
274
275    /// NSPD - Not Specified.
276    #[cfg_attr(feature = "serde", serde(alias = "NSPD"))]
277    NotSpecified,
278
279    /// OTFS - Organised Trading Facility.
280    #[cfg_attr(feature = "serde", serde(alias = "OTFS"))]
281    OrganisedTradingFacility,
282
283    /// OTHR - Other.
284    #[cfg_attr(feature = "serde", serde(alias = "OTHR"))]
285    Other,
286
287    /// RMOS - Recognised Market Operator.
288    #[cfg_attr(feature = "serde", serde(alias = "RMOS"))]
289    RecognisedMarketOperator,
290
291    /// RMKT - Regulated Market.
292    #[cfg_attr(feature = "serde", serde(alias = "RMKT"))]
293    RegulatedMarket,
294
295    /// SEFS - Swap Execution Facility.
296    #[cfg_attr(feature = "serde", serde(alias = "SEFS"))]
297    SwapExecutionFacility,
298
299    /// SINT - Systematic Internaliser.
300    #[cfg_attr(feature = "serde", serde(alias = "SINT"))]
301    SystematicInternaliser,
302
303    /// TRFS - Trade Reporting Facility.
304    #[cfg_attr(feature = "serde", serde(alias = "TRFS"))]
305    TradeReportingFacility,
306}
307
308/// The status of a MIC.
309#[derive(Copy, Clone, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)]
310#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
311pub enum Status {
312    /// The MIC is active.
313    #[cfg_attr(feature = "serde", serde(alias = "ACTIVE"))]
314    Active,
315
316    /// The MIC has been updated.
317    #[cfg_attr(feature = "serde", serde(alias = "UPDATED"))]
318    Updated,
319
320    /// The MIC has expired.
321    #[cfg_attr(feature = "serde", serde(alias = "EXPIRED"))]
322    Expired,
323}