rateslib/fx/rates/
ccy.rs

1use internment::Intern;
2use pyo3::exceptions::PyValueError;
3use pyo3::{pyclass, PyErr};
4use serde::{Deserialize, Serialize};
5
6/// A currency identified by 3-ascii ISO code.
7#[pyclass(module = "rateslib.rs")]
8#[derive(Copy, Clone, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)]
9pub struct Ccy {
10    pub(crate) name: Intern<String>,
11}
12
13impl Ccy {
14    /// Constructs a new `Ccy`.
15    ///
16    /// Use **only** 3-ascii names. e.g. *"usd"*, aligned with ISO representation. `name` is converted
17    /// to lowercase to promote performant equality between "USD" and "usd".
18    ///
19    /// Panics if `name` is not 3 bytes in length.
20    pub fn try_new(name: &str) -> Result<Self, PyErr> {
21        let ccy: String = name.to_string().to_lowercase();
22        if ccy.len() != 3 {
23            return Err(PyValueError::new_err(
24                "`Ccy` must be 3 ascii character in length, e.g. 'usd'.",
25            ));
26        }
27        Ok(Ccy {
28            name: Intern::new(ccy),
29        })
30    }
31}
32
33#[cfg(test)]
34mod tests {
35    use super::*;
36
37    #[test]
38    fn ccy_creation() {
39        let a = Ccy::try_new("usd").unwrap();
40        let b = Ccy::try_new("USD").unwrap();
41        assert_eq!(a, b)
42    }
43
44    #[test]
45    fn ccy_creation_error() {
46        match Ccy::try_new("FOUR") {
47            Ok(_) => assert!(false),
48            Err(_) => assert!(true),
49        }
50    }
51}