polyoxide_binance/shared/
symbol.rs1use std::{fmt, str::FromStr};
4
5#[derive(Debug, Clone, PartialEq, Eq, Hash, PartialOrd, Ord)]
18pub struct Symbol(String);
19
20#[derive(Debug, Clone, PartialEq, Eq, thiserror::Error)]
22#[error("{0:?} is not a Binance symbol: 1 to 32 letters, digits or underscores")]
23pub struct InvalidSymbol(pub String);
24
25impl polyoxide_venue::Classify for InvalidSymbol {
27 fn class(&self) -> polyoxide_venue::Class {
28 polyoxide_venue::Class::InvalidRequest
29 }
30}
31
32impl Symbol {
33 pub const MAX_LEN: usize = 32;
35
36 pub fn new(symbol: impl Into<String>) -> Result<Self, InvalidSymbol> {
38 let symbol = symbol.into();
39 let chars = symbol.chars().count();
40 let valid = (1..=Self::MAX_LEN).contains(&chars)
41 && symbol.chars().all(|c| c.is_alphanumeric() || c == '_');
42 if valid {
43 Ok(Self(symbol.to_ascii_uppercase()))
44 } else {
45 Err(InvalidSymbol(symbol))
46 }
47 }
48
49 pub fn as_str(&self) -> &str {
51 &self.0
52 }
53}
54
55impl fmt::Display for Symbol {
56 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
57 f.write_str(&self.0)
58 }
59}
60
61impl FromStr for Symbol {
62 type Err = InvalidSymbol;
63
64 fn from_str(s: &str) -> Result<Self, Self::Err> {
65 Self::new(s)
66 }
67}
68
69impl AsRef<str> for Symbol {
70 fn as_ref(&self) -> &str {
71 &self.0
72 }
73}