Skip to main content

oanda_client/
ids.rs

1//! Validated OANDA provider identifiers.
2
3use serde::{Deserialize, Deserializer, Serialize, Serializer};
4use std::{fmt, str::FromStr};
5
6/// Error returned when a provider identifier is empty or contains control characters.
7#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
8#[error("invalid OANDA identifier")]
9pub struct InvalidId;
10
11macro_rules! id_type {
12    ($name:ident) => {
13        #[doc = concat!("Validated OANDA ", stringify!($name), ".")]
14        #[derive(Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
15        pub struct $name(String);
16
17        impl $name {
18            /// Validate and construct an identifier.
19            ///
20            /// # Errors
21            ///
22            /// Returns [`InvalidId`] for an empty identifier or one containing control characters.
23            pub fn new(value: impl Into<String>) -> Result<Self, InvalidId> {
24                let value = value.into();
25                if value.is_empty() || value.chars().any(char::is_control) {
26                    return Err(InvalidId);
27                }
28                Ok(Self(value))
29            }
30
31            /// Return the provider spelling.
32            pub fn as_str(&self) -> &str {
33                &self.0
34            }
35        }
36
37        impl fmt::Debug for $name {
38            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39                f.debug_tuple(stringify!($name)).field(&self.0).finish()
40            }
41        }
42        impl fmt::Display for $name {
43            fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
44                f.write_str(&self.0)
45            }
46        }
47        impl FromStr for $name {
48            type Err = InvalidId;
49            fn from_str(s: &str) -> Result<Self, Self::Err> {
50                Self::new(s)
51            }
52        }
53        impl Serialize for $name {
54            fn serialize<S: Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
55                serializer.serialize_str(&self.0)
56            }
57        }
58        impl<'de> Deserialize<'de> for $name {
59            fn deserialize<D: Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
60                let value = String::deserialize(deserializer)?;
61                Self::new(value).map_err(serde::de::Error::custom)
62            }
63        }
64    };
65}
66
67id_type!(AccountID);
68id_type!(OrderID);
69id_type!(TradeID);
70id_type!(TransactionID);
71id_type!(InstrumentName);
72id_type!(OrderSpecifier);
73id_type!(TradeSpecifier);
74id_type!(ClientID);
75id_type!(ClientTag);
76id_type!(ClientComment);
77id_type!(RequestID);
78id_type!(ClientRequestID);
79id_type!(Currency);
80id_type!(CandleSpecification);
81id_type!(PricingComponent);