1use rust_decimal::Decimal;
2
3mod core;
4pub mod encoding;
5pub mod error;
6
7pub use encoding::{Ebcdic, Encoding, Sign};
8pub use error::Error;
9
10static EBCDIC_INSTANCE: Ebcdic = Ebcdic;
11
12pub fn extract(raw: &str, decimals: usize) -> Result<Decimal, Error> {
13 core::extract_with_encoding(raw, decimals, &EBCDIC_INSTANCE)
14}
15
16pub fn format(value: Decimal, decimals: usize) -> Result<String, Error> {
17 if value.is_zero() {
19 let is_negative_zero = std::env::var("HANDLE_NEGATIVE_ZERO").unwrap_or_default() == "true";
21
22 if decimals == 0 {
23 return if is_negative_zero {
25 Ok("}".to_string()) } else {
27 Ok("{".to_string()) };
29 } else {
30 let mut result = String::new();
32 for _ in 0..decimals {
33 result.push('0');
34 }
35
36 if is_negative_zero {
38 result.push('}'); } else {
40 result.push('{'); }
42
43 return Ok(result);
44 }
45 }
46
47 core::format_with_encoding(value, decimals, &EBCDIC_INSTANCE)
49}
50
51pub fn convert_from_signed_format(value: &str, field_format: &str) -> Result<Decimal, Error> {
52 let decimals = parse_format(field_format)?;
53 core::extract_with_encoding(value, decimals, &EBCDIC_INSTANCE)
54}
55
56pub fn convert_to_signed_format(value: Decimal, field_format: &str) -> Result<String, Error> {
57 let decimals = parse_format(field_format)?;
58 core::format_with_encoding(value, decimals, &EBCDIC_INSTANCE)
59}
60
61pub fn extract_with_encoding<E: Encoding>(
62 raw: &str,
63 decimals: usize,
64 encoding: &E,
65) -> Result<Decimal, Error> {
66 core::extract_with_encoding(raw, decimals, encoding)
67}
68
69pub fn format_with_encoding<E: Encoding>(
70 value: Decimal,
71 decimals: usize,
72 encoding: &E,
73) -> Result<String, Error> {
74 core::format_with_encoding(value, decimals, encoding)
75}
76
77pub fn extract_with_dyn_encoding(
78 raw: &str,
79 decimals: usize,
80 encoding: &dyn Encoding,
81) -> Result<Decimal, Error> {
82 core::extract_with_encoding(raw, decimals, encoding)
83}
84
85pub fn format_with_dyn_encoding(
86 value: Decimal,
87 decimals: usize,
88 encoding: &dyn Encoding,
89) -> Result<String, Error> {
90 core::format_with_encoding(value, decimals, encoding)
91}
92
93fn parse_format(field_format: &str) -> Result<usize, Error> {
94 if field_format.contains('(') && field_format.contains(')') {
96 if let Some(pos) = field_format.find(['v', 'V']) {
97 let decimal_part = &field_format[pos + 1..];
99
100 if let Some(start) = decimal_part.find("9(") {
102 let end = decimal_part
103 .find(')')
104 .ok_or_else(|| Error::InvalidFormatString(field_format.to_string()))?;
105 if start + 2 < end {
106 let count_str = &decimal_part[start + 2..end];
107 if let Ok(count) = count_str.parse::<usize>() {
108 return Ok(count);
109 }
110 }
111 } else if decimal_part.chars().all(|c| c == '9') {
112 return Ok(decimal_part.len());
114 } else if decimal_part.is_empty() {
115 return Ok(0);
116 }
117 } else {
118 if field_format.starts_with('s') || field_format.starts_with('9') {
120 return Ok(0); }
122 }
123 } else if let Some(pos) = field_format.find(['v', 'V']) {
124 let decimal_part = &field_format[pos + 1..];
126 if decimal_part.chars().all(|c| c == '9') {
127 return Ok(decimal_part.len());
128 } else if decimal_part.is_empty() {
129 return Ok(0);
130 }
131 }
132
133 Err(Error::InvalidFormatString(field_format.to_string()))
134}