1use crate::error::{ISO8583Error, Result};
9
10#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum Encoding {
13 ASCII,
15 BCD,
17 EBCDIC,
19}
20
21pub fn encode_bcd(s: &str) -> Result<Vec<u8>> {
29 if !s.chars().all(|c| c.is_ascii_digit()) {
30 return Err(ISO8583Error::EncodingError(format!(
31 "BCD encoding requires numeric input, got: {}",
32 s
33 )));
34 }
35
36 let mut padded = s.to_string();
37 if padded.len() % 2 != 0 {
38 padded.insert(0, '0');
39 }
40
41 let mut result = Vec::with_capacity(padded.len() / 2);
42
43 for chunk in padded.as_bytes().chunks(2) {
44 let high = (chunk[0] - b'0') << 4;
45 let low = chunk[1] - b'0';
46 result.push(high | low);
47 }
48
49 Ok(result)
50}
51
52pub fn decode_bcd(bytes: &[u8], length: usize) -> Result<String> {
54 let mut result = String::with_capacity(length);
55
56 for &byte in bytes {
57 let high = (byte >> 4) & 0x0F;
58 let low = byte & 0x0F;
59
60 if high > 9 || low > 9 {
61 return Err(ISO8583Error::EncodingError(format!(
62 "Invalid BCD byte: 0x{:02X}",
63 byte
64 )));
65 }
66
67 result.push((b'0' + high) as char);
68 result.push((b'0' + low) as char);
69
70 if result.len() >= length {
71 break;
72 }
73 }
74
75 result.truncate(length);
77
78 Ok(result)
79}
80
81pub fn encode_ascii(s: &str) -> Vec<u8> {
83 s.as_bytes().to_vec()
84}
85
86pub fn decode_ascii(bytes: &[u8]) -> Result<String> {
88 std::str::from_utf8(bytes)
89 .map(|s| s.to_string())
90 .map_err(|e| ISO8583Error::EncodingError(format!("Invalid ASCII: {}", e)))
91}
92
93const EBCDIC_TO_ASCII: &[u8; 256] = &[
95 0x00, 0x01, 0x02, 0x03, 0x9C, 0x09, 0x86, 0x7F, 0x97, 0x8D, 0x8E, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x9D, 0x85, 0x08, 0x87, 0x18, 0x19, 0x92, 0x8F, 0x1C, 0x1D, 0x1E, 0x1F, 0x80, 0x81, 0x82, 0x83, 0x84, 0x0A, 0x17, 0x1B, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x05, 0x06, 0x07, 0x90, 0x91, 0x16, 0x93, 0x94, 0x95, 0x96, 0x04, 0x98, 0x99, 0x9A, 0x9B, 0x14, 0x15, 0x9E, 0x1A, 0x20, 0xA0, 0xE2, 0xE4, 0xE0, 0xE1, 0xE3, 0xE5, 0xE7, 0xF1, 0xA2, 0x2E, 0x3C, 0x28, 0x2B, 0x7C, 0x26, 0xE9, 0xEA, 0xEB, 0xE8, 0xED, 0xEE, 0xEF, 0xEC, 0xDF, 0x21, 0x24, 0x2A, 0x29, 0x3B, 0xAC, 0x2D, 0x2F, 0xC2, 0xC4, 0xC0, 0xC1, 0xC3, 0xC5, 0xC7, 0xD1, 0xA6, 0x2C, 0x25, 0x5F, 0x3E, 0x3F, 0xF8, 0xC9, 0xCA, 0xCB, 0xC8, 0xCD, 0xCE, 0xCF, 0xCC, 0x60, 0x3A, 0x23, 0x40, 0x27, 0x3D, 0x22, 0xD8, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0xAB, 0xBB, 0xF0, 0xFD, 0xFE, 0xB1, 0xB0, 0x6A, 0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0xAA, 0xBA, 0xE6, 0xB8, 0xC6, 0xA4, 0xB5, 0x7E, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x7A, 0xA1, 0xBF, 0xD0, 0xDD, 0xDE, 0xAE, 0x5E, 0xA3, 0xA5, 0xB7, 0xA9, 0xA7, 0xB6, 0xBC, 0xBD, 0xBE, 0x5B, 0x5D, 0xAF, 0xA8, 0xB4, 0xD7, 0x7B, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0xAD, 0xF4, 0xF6, 0xF2, 0xF3, 0xF5, 0x7D, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52, 0xB9, 0xFB, 0xFC, 0xF9, 0xFA, 0xFF, 0x5C, 0xF7, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0xB2, 0xD4, 0xD6, 0xD2, 0xD3, 0xD5, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0xB3, 0xDB, 0xDC, 0xD9, 0xDA, 0x9F, ];
128
129pub fn encode_ebcdic(s: &str) -> Result<Vec<u8>> {
131 let mut result = Vec::with_capacity(s.len());
132
133 for byte in s.as_bytes() {
134 let ebcdic = EBCDIC_TO_ASCII
136 .iter()
137 .position(|&b| b == *byte)
138 .ok_or_else(|| {
139 ISO8583Error::EncodingError(format!("Cannot encode byte to EBCDIC: 0x{:02X}", byte))
140 })?;
141
142 result.push(ebcdic as u8);
143 }
144
145 Ok(result)
146}
147
148pub fn decode_ebcdic(bytes: &[u8]) -> Result<String> {
150 let ascii_bytes: Vec<u8> = bytes.iter().map(|&b| EBCDIC_TO_ASCII[b as usize]).collect();
151
152 decode_ascii(&ascii_bytes)
153}
154
155pub fn encode_length(length: usize, digits: usize, encoding: Encoding) -> Result<Vec<u8>> {
157 let length_str = format!("{:0width$}", length, width = digits);
158
159 match encoding {
160 Encoding::ASCII => Ok(encode_ascii(&length_str)),
161 Encoding::BCD => encode_bcd(&length_str),
162 Encoding::EBCDIC => encode_ebcdic(&length_str),
163 }
164}
165
166pub fn decode_length(bytes: &[u8], digits: usize, encoding: Encoding) -> Result<usize> {
168 let length_str = match encoding {
169 Encoding::ASCII => decode_ascii(bytes)?,
170 Encoding::BCD => decode_bcd(bytes, digits)?,
171 Encoding::EBCDIC => decode_ebcdic(bytes)?,
172 };
173
174 length_str
175 .parse()
176 .map_err(|e| ISO8583Error::EncodingError(format!("Invalid length value: {}", e)))
177}
178
179#[cfg(test)]
180mod tests {
181 use super::*;
182
183 #[test]
184 fn test_bcd_encoding() {
185 let encoded = encode_bcd("1234").unwrap();
186 assert_eq!(encoded, vec![0x12, 0x34]);
187
188 let encoded = encode_bcd("123").unwrap();
189 assert_eq!(encoded, vec![0x01, 0x23]);
190 }
191
192 #[test]
193 fn test_bcd_decoding() {
194 let decoded = decode_bcd(&[0x12, 0x34], 4).unwrap();
195 assert_eq!(decoded, "1234");
196
197 let decoded = decode_bcd(&[0x01, 0x23], 3).unwrap();
198 assert_eq!(decoded, "012");
199 }
200
201 #[test]
202 fn test_ascii_encoding() {
203 let encoded = encode_ascii("Hello");
204 assert_eq!(encoded, b"Hello");
205 }
206
207 #[test]
208 fn test_ascii_decoding() {
209 let decoded = decode_ascii(b"Hello").unwrap();
210 assert_eq!(decoded, "Hello");
211 }
212
213 #[test]
214 fn test_length_encoding_ascii() {
215 let encoded = encode_length(12, 2, Encoding::ASCII).unwrap();
216 assert_eq!(encoded, b"12");
217
218 let encoded = encode_length(123, 3, Encoding::ASCII).unwrap();
219 assert_eq!(encoded, b"123");
220 }
221
222 #[test]
223 fn test_length_decoding_ascii() {
224 let decoded = decode_length(b"12", 2, Encoding::ASCII).unwrap();
225 assert_eq!(decoded, 12);
226
227 let decoded = decode_length(b"123", 3, Encoding::ASCII).unwrap();
228 assert_eq!(decoded, 123);
229 }
230
231 #[test]
232 fn test_length_encoding_bcd() {
233 let encoded = encode_length(12, 2, Encoding::BCD).unwrap();
234 assert_eq!(encoded, vec![0x12]);
235 }
236
237 #[test]
238 fn test_invalid_bcd_input() {
239 assert!(encode_bcd("12A4").is_err());
240 assert!(encode_bcd("ABCD").is_err());
241 }
242
243 #[test]
244 fn test_ebcdic_numbers() {
245 let encoded = encode_ebcdic("0123456789").unwrap();
247 let decoded = decode_ebcdic(&encoded).unwrap();
248 assert_eq!(decoded, "0123456789");
249 }
250
251 #[test]
252 fn test_ebcdic_letters() {
253 let encoded = encode_ebcdic("ABCDEFGHIJKLMNOPQRSTUVWXYZ").unwrap();
255 let decoded = decode_ebcdic(&encoded).unwrap();
256 assert_eq!(decoded, "ABCDEFGHIJKLMNOPQRSTUVWXYZ");
257 }
258}