1#[derive(Debug, PartialEq)]
7pub enum ParseError {
8 EmptyInput,
9 InvalidFormat,
10}
11
12pub fn format_naira<T>(amount: T) -> String
13where
14 T: Into<f64>,
15{
16 let value = amount.into();
17 let sign = if value < 0.0 { "-" } else { "" };
18 let absolute_value = value.abs();
19
20 let whole = absolute_value.trunc() as i64;
23 let decimal_part = ((absolute_value - absolute_value.trunc()) * 100.0).round() as u32;
24
25 let formatted_whole = format_with_commas(whole);
26
27 format!("₦{}{}.{:02}", sign, formatted_whole, decimal_part)
28}
29
30pub fn format_kobo_to_naira<T>(amount: T) -> String
31where
32 T: Into<f64>,
33{
34 let value = amount.into();
35 let sign = if value < 0.0 { "-" } else { "" };
36 let abs_kobo = value.abs().round() as i64;
37
38 let whole_naira = abs_kobo / 100;
40 let decimal_part = abs_kobo % 100;
41
42 let formatted_whole = format_with_commas(whole_naira);
44
45 format!("₦{}{}.{:02}", sign, formatted_whole, decimal_part)
46}
47
48pub fn format_naira_to_kobo<T>(amount: T) -> String
49where
50 T: Into<f64>,
51{
52 let value = amount.into();
53 let sign = if value < 0.0 { "-" } else { "" };
54 let kobo_amount = (value.abs() * 100.0).round() as i64;
55 format!("{}{}", sign, kobo_amount)
56}
57
58pub fn format_naira_compact<T>(amount: T) -> String
59where
60 T: Into<f64>,
61{
62 let value = amount.into();
63 let sign = if value < 0.0 { "-" } else { "" };
64 let absolute_value = value.abs();
65 if absolute_value < 1_000.0 {
66 let whole = absolute_value.trunc() as i64;
68 let decimal_part = ((absolute_value - absolute_value.trunc()) * 100.0).round() as u32;
69 if decimal_part == 0 {
70 format!("₦{}{}", sign, whole)
71 } else {
72 format!("₦{}{}.{:02}", sign, whole, decimal_part)
73 }
74 } else if absolute_value < 1_000_000.0 {
75 let thousands = absolute_value / 1_000.0;
77 let whole_k = thousands.trunc() as i64;
78 let decimal_k = ((thousands - thousands.trunc()) * 10.0).round() as u32;
79 if decimal_k == 0 {
80 format!("₦{}{}K", sign, whole_k)
81 } else {
82 format!("₦{}{}.{}K", sign, whole_k, decimal_k)
83 }
84 } else if absolute_value < 1_000_000_000.0 {
85 let millions = absolute_value / 1_000_000.0;
87 let whole_m = millions.trunc() as i64;
88 let decimal_m = ((millions - millions.trunc()) * 10.0).round() as u32;
89 if decimal_m == 0 {
90 format!("₦{}{}M", sign, whole_m)
91 } else {
92 format!("₦{}{}.{}M", sign, whole_m, decimal_m)
93 }
94 } else {
95 let billions = absolute_value / 1_000_000_000.0;
97 let whole_b = billions.trunc() as i64;
98 let decimal_b = ((billions - billions.trunc()) * 10.0).round() as u32;
99 if decimal_b == 0 {
100 format!("₦{}{}B", sign, whole_b)
101 } else {
102 format!("₦{}{}.{}B", sign, whole_b, decimal_b)
103 }
104 }
105}
106
107pub fn parse_naira(input: &str) -> Result<f64, ParseError> {
108 let trimmed = input.trim();
109
110 if trimmed.is_empty() {
111 return Err(ParseError::EmptyInput);
112 }
113
114 let (sign, value) = if let Some(stripped) = trimmed.strip_prefix('-') {
116 (-1.0, stripped)
117 } else {
118 (1.0, trimmed)
119 };
120
121 let cleaned = value.replace(['₦', ','], "");
123
124 let number: f64 = cleaned.parse().map_err(|_| ParseError::InvalidFormat)?;
126
127 Ok(sign * number)
128}
129
130fn format_with_commas(n: i64) -> String {
131 let s = n.to_string();
132 let sbyte = s.as_bytes();
133 let mut result = String::new();
134
135 let len = sbyte.len();
136 for (i, &b) in sbyte.iter().enumerate() {
137 if i > 0 && (len - i) % 3 == 0 {
138 result.push(',');
139 }
140 result.push(b as char);
141 }
142
143 result
144}
145
146#[cfg(test)]
147mod tests {
148 use super::*;
149
150 #[test]
151 fn test_format_naira() {
152 assert_eq!(format_naira(1234567.89), "₦1,234,567.89");
153 assert_eq!(format_naira(-55000), "₦-55,000.00");
154 }
155
156 #[test]
157 fn test_commas() {
158 assert_eq!(format_with_commas(1), "1");
159 assert_eq!(format_with_commas(1234567), "1,234,567");
160 assert_eq!(format_with_commas(-55000), "-55,000");
161 }
162
163 #[test]
164 fn test_format_kobo_to_naira() {
165 assert_eq!(format_kobo_to_naira(1), "₦0.01");
166 assert_eq!(format_kobo_to_naira(50), "₦0.50");
167 assert_eq!(format_kobo_to_naira(100), "₦1.00");
168 assert_eq!(format_kobo_to_naira(150000), "₦1,500.00");
169 assert_eq!(format_kobo_to_naira(-7550), "₦-75.50");
170 }
171
172 #[test]
173 fn test_format_naira_to_kobo() {
174 assert_eq!(format_naira_to_kobo(0.01), "1");
175 assert_eq!(format_naira_to_kobo(1234567.89), "123456789");
176 assert_eq!(format_naira_to_kobo(-75.50), "-7550");
177 }
178
179 #[test]
180 fn test_format_naira_compact() {
181 assert_eq!(format_naira_compact(950), "₦950");
182 assert_eq!(format_naira_compact(1200), "₦1.2K");
183 assert_eq!(format_naira_compact(2500000), "₦2.5M");
184 }
185 #[test]
186 fn test_parse_naira() {
187 assert_eq!(parse_naira("₦1,500.50"), Ok(1500.5));
188 assert_eq!(parse_naira("1500"), Ok(1500.0));
189 assert_eq!(parse_naira("1,234"), Ok(1234.0));
190 assert_eq!(parse_naira("-₦75.50"), Ok(-75.5));
191 assert_eq!(parse_naira(""), Err(ParseError::EmptyInput));
192 assert_eq!(parse_naira("abc"), Err(ParseError::InvalidFormat));
193 }
194}