near_token/trait_impls/
from_str.rs1use crate::{NearToken, NearTokenError, ONE_MICRONEAR, ONE_MILLINEAR, ONE_NEAR};
2
3impl std::str::FromStr for NearToken {
4 type Err = NearTokenError;
5 fn from_str(s: &str) -> Result<Self, Self::Err> {
6 let uppercase_s = s.trim().to_ascii_uppercase();
7 let (value, unit) = uppercase_s.split_at(
8 s.find(|c: char| c.is_ascii_alphabetic())
9 .ok_or_else(|| NearTokenError::InvalidTokenUnit(s.to_owned()))?,
10 );
11 let unit_precision = match unit {
12 "YN" | "YNEAR" | "YOCTONEAR" => 1,
13 "MICRONEAR" => ONE_MICRONEAR,
14 "MILLINEAR" => ONE_MILLINEAR,
15 "NEAR" | "N" => ONE_NEAR,
16 _ => return Err(NearTokenError::InvalidTokenUnit(s.to_owned())),
17 };
18 Ok(NearToken::from_yoctonear(
19 crate::utils::parse_decimal_number(value.trim(), unit_precision)
20 .map_err(NearTokenError::InvalidTokensAmount)?,
21 ))
22 }
23}
24
25#[cfg(test)]
26mod test {
27 use std::str::FromStr;
28
29 use crate::{DecimalNumberParsingError, NearToken, NearTokenError};
30
31 #[test]
32 fn parse_decimal_number() {
33 let data = "0.123456 near";
34 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
35 assert_eq!(
36 gas.unwrap(),
37 NearToken::from_yoctonear(123456000000000000000000)
38 );
39 }
40 #[test]
41 fn parse_number_with_decimal_part() {
42 let data = "11.123456 near";
43 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
44 assert_eq!(
45 gas.unwrap(),
46 NearToken::from_yoctonear(11123456000000000000000000)
47 );
48 }
49
50 #[test]
51 fn parse_yocto_number() {
52 let data = "123456 YN";
53 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
54 assert_eq!(gas.unwrap(), NearToken::from_yoctonear(123456));
55 }
56
57 #[test]
58 fn parse_micro_number() {
59 let data = "123456 microNEAR";
60 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
61 assert_eq!(gas.unwrap(), NearToken::from_micronear(123456));
62 }
63
64 #[test]
65 fn parse_milli_number() {
66 let data = "123456 milliNEAR";
67 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
68 assert_eq!(gas.unwrap(), NearToken::from_millinear(123456));
69 }
70
71 #[test]
72 fn doubledot() {
73 let data = "1.1.1 Near";
74 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
75 assert_eq!(
76 gas,
77 Err(NearTokenError::InvalidTokensAmount(
78 DecimalNumberParsingError::InvalidNumber("1.1.1".to_owned())
79 ))
80 )
81 }
82
83 #[test]
84 fn space_after_dot() {
85 let data = "1. 0 near";
86 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
87 assert_eq!(
88 gas,
89 Err(NearTokenError::InvalidTokensAmount(
90 DecimalNumberParsingError::InvalidNumber("1. 0".to_owned())
91 ))
92 )
93 }
94
95 #[test]
96 fn incorect_currency() {
97 let data = "0 pas";
98 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
99 assert_eq!(gas, Err(NearTokenError::InvalidTokenUnit(data.to_owned())))
100 }
101
102 #[test]
103 fn without_currency() {
104 let data = "0";
105 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
106 assert_eq!(gas, Err(NearTokenError::InvalidTokenUnit("0".to_owned())))
107 }
108
109 #[test]
110 fn invalid_whole() {
111 let data = "-1 Near";
112 let gas: Result<NearToken, NearTokenError> = FromStr::from_str(data);
113 assert_eq!(
114 gas,
115 Err(NearTokenError::InvalidTokensAmount(
116 DecimalNumberParsingError::InvalidNumber("-1".to_owned())
117 ))
118 )
119 }
120
121 #[test]
122 fn test_from_str_f64_gas_without_int() {
123 let near_gas = NearToken::from_str(".055 ynear").unwrap_err();
124 assert_eq!(
125 near_gas,
126 NearTokenError::InvalidTokensAmount(DecimalNumberParsingError::InvalidNumber(
127 ".055".to_string()
128 ))
129 );
130 }
131
132 #[test]
133 fn test_from_str_without_unit() {
134 let near_gas = NearToken::from_str("100").unwrap_err();
135 assert_eq!(
136 near_gas,
137 NearTokenError::InvalidTokenUnit("100".to_string())
138 );
139 }
140
141 #[test]
142 fn test_from_str_incorrect_unit() {
143 let near_gas = NearToken::from_str("100 UAH").unwrap_err();
144 assert_eq!(
145 near_gas,
146 NearTokenError::InvalidTokenUnit("100 UAH".to_string())
147 );
148 }
149
150 #[test]
151 fn test_from_str_invalid_double_dot() {
152 let near_gas = NearToken::from_str("100.55.").unwrap_err();
153 assert_eq!(
154 near_gas,
155 NearTokenError::InvalidTokenUnit("100.55.".to_string())
156 );
157 }
158
159 #[test]
160 fn test_from_str_large_fractional_part() {
161 let near_gas = NearToken::from_str("100.1111122222333 ynear").unwrap_err(); assert_eq!(
163 near_gas,
164 NearTokenError::InvalidTokensAmount(DecimalNumberParsingError::LongFractional(
165 "1111122222333".to_string()
166 ))
167 );
168 }
169}