1use core::marker::PhantomData;
2
3use alloc::{
4 format,
5 string::{String, ToString},
6 vec::Vec,
7};
8use encoding::Encoding;
9
10use crate::{
11 CustomRequisites, NoCustomRequisites, Payment, PaymentEncoding, PaymentHeader, Requisite,
12 FORMAT_ID_BYTES, VERSION_0001_BYTES,
13};
14
15pub trait ParserStrategy<T: CustomRequisites> {
17 fn parse_from_str(&self, val: &str) -> super::Result<Payment<T>>;
21
22 fn parse_from_bytes(&self, bytes: &[u8]) -> super::Result<Payment<T>>;
24}
25
26#[derive(Debug)]
28pub struct PaymentParser<
29 T: ParserStrategyType = StrictParser,
30 RT: CustomRequisites = NoCustomRequisites,
31> {
32 version_id: [u8; 4],
33 _req_marker: PhantomData<RT>,
34 _marker: PhantomData<T>,
35}
36
37impl<T: ParserStrategyType, RT: CustomRequisites> PaymentParser<T, RT> {
38 pub fn with_version(mut self, version_id: [u8; 4]) -> Self {
40 self.version_id = version_id;
41 self
42 }
43}
44
45impl<RT: CustomRequisites> ParserStrategy<RT> for PaymentParser<StrictParser, RT> {
46 fn parse_from_str(&self, val: &str) -> crate::Result<Payment<RT>> {
47 let header = self.read_payment_header(val, true)?;
48
49 let data = val[8..].to_string();
50
51 let requisites = self.read_requisites(&data, header.separator as char)?;
52
53 self.validate_required_requisites(&requisites)?;
54
55 Ok(Payment { header, requisites })
56 }
57
58 fn parse_from_bytes(&self, bytes: &[u8]) -> crate::Result<Payment<RT>> {
59 let header = self.read_payment_header_bytes(bytes)?;
60
61 let data = self.decode_payment_body(
62 header.encoding,
63 &bytes[8..],
64 encoding::DecoderTrap::Strict,
65 |val| String::from_utf8(val.to_vec()).map_err(|_| super::Error::DecodingError),
66 )?;
67
68 let requisites = self.read_requisites(&data, header.separator as char)?;
69
70 self.validate_required_requisites(&requisites)?;
71
72 Ok(Payment { header, requisites })
73 }
74}
75
76impl<RT: CustomRequisites> PaymentParser<StrictParser, RT> {
77 fn read_requisites(&self, data: &str, separator: char) -> super::Result<Vec<Requisite<RT>>> {
78 let kv = data.split(separator);
79
80 kv.into_iter()
81 .map(|kv| kv.split_once('=').ok_or(super::Error::WrongPair))
82 .flat_map(|kv| kv.map(|kv| kv.try_into()))
83 .collect()
84 }
85}
86
87impl<RT: CustomRequisites> ParserStrategy<RT> for PaymentParser<RequisiteToleranceParser, RT> {
88 fn parse_from_str(&self, val: &str) -> crate::Result<Payment<RT>> {
89 let header = self.read_payment_header(val, true)?;
90
91 let data = val[8..].to_string();
92
93 let requisites = self.read_requisites(&data, header.separator as char);
94
95 self.validate_required_requisites(&requisites)?;
96
97 Ok(Payment { header, requisites })
98 }
99
100 fn parse_from_bytes(&self, bytes: &[u8]) -> crate::Result<Payment<RT>> {
101 let header = self.read_payment_header_bytes(bytes)?;
102
103 let data = self.decode_payment_body(
104 header.encoding,
105 &bytes[8..],
106 encoding::DecoderTrap::Strict,
107 |val| String::from_utf8(val.to_vec()).map_err(|_| super::Error::DecodingError),
108 )?;
109
110 let requisites = self.read_requisites(&data, header.separator as char);
111
112 self.validate_required_requisites(&requisites)?;
113
114 Ok(Payment { header, requisites })
115 }
116}
117
118impl<RT: CustomRequisites> PaymentParser<RequisiteToleranceParser, RT> {
119 fn read_requisites(&self, data: &str, separator: char) -> Vec<Requisite<RT>> {
120 let kv = data.split(separator);
121
122 kv.into_iter()
123 .flat_map(|kv| kv.split_once('='))
124 .flat_map(|kv| kv.try_into())
125 .collect()
126 }
127}
128
129impl<RT: CustomRequisites> ParserStrategy<RT> for PaymentParser<LooseParser, RT> {
130 fn parse_from_str(&self, val: &str) -> crate::Result<Payment<RT>> {
131 let header = self.read_payment_header(val, false)?;
132
133 let data = val[8..].to_string();
134
135 let requisites = self.read_requisites(&data, header.separator as char);
136
137 Ok(Payment { header, requisites })
138 }
139
140 fn parse_from_bytes(&self, bytes: &[u8]) -> crate::Result<Payment<RT>> {
141 let header = self.read_payment_header_bytes(bytes)?;
142
143 let data = self.decode_payment_body(
144 header.encoding,
145 &bytes[8..],
146 encoding::DecoderTrap::Replace,
147 |val| Ok(String::from_utf8_lossy(val).to_string()),
148 )?;
149
150 let requisites = self.read_requisites(&data, header.separator as char);
151
152 Ok(Payment { header, requisites })
153 }
154}
155
156impl<RT: CustomRequisites> PaymentParser<LooseParser, RT> {
157 fn read_requisites(&self, data: &str, separator: char) -> Vec<Requisite<RT>> {
158 let kv = data.split(separator);
159
160 kv.into_iter()
161 .flat_map(|kv| kv.split_once('='))
162 .flat_map(|kv| kv.try_into())
163 .collect()
164 }
165}
166
167impl<T: ParserStrategyType, RT: CustomRequisites> PaymentParser<T, RT> {
168 fn read_payment_header_bytes(&self, bytes: &[u8]) -> super::Result<PaymentHeader> {
169 if bytes.len() < 8 {
170 return Err(super::Error::CorruptedHeader(
171 "Не возможно сформировать заголовок, так как длина меньше 8".into(),
172 ));
173 }
174
175 let format_id = &bytes[0..2];
176
177 if format_id != FORMAT_ID_BYTES {
178 return Err(super::Error::WrongFormatId([format_id[0], format_id[1]]));
179 }
180
181 let version = &bytes[2..6];
182 if version != self.version_id {
183 return Err(super::Error::UnsupportedVersion {
184 passed: [version[0], version[1], version[2], version[3]],
185 current: self.version_id,
186 });
187 }
188
189 let encoding: PaymentEncoding = bytes[6].try_into()?;
190 let separator = bytes[7];
191
192 Ok(PaymentHeader {
193 format_id: FORMAT_ID_BYTES,
194 version: self.version_id,
195 encoding,
196 separator,
197 })
198 }
199
200 fn read_payment_header(&self, val: &str, check_encoding: bool) -> super::Result<PaymentHeader> {
201 let bytes = val.chars().take(8).map(|c| c as u8).collect::<Vec<_>>();
202 let header = self.read_payment_header_bytes(&bytes)?;
203
204 if check_encoding && header.encoding != PaymentEncoding::Utf8 {
205 return Err(super::Error::CorruptedHeader(
206 format!(
207 "Не верная кодировка, должна быть Utf-8, установлена {}",
208 header.encoding
209 )
210 .into(),
211 ));
212 }
213
214 Ok(header)
215 }
216
217 fn decode_payment_body(
218 &self,
219 encoding: PaymentEncoding,
220 bytes: &[u8],
221 trap: encoding::DecoderTrap,
222 utf8_decode: fn(&[u8]) -> super::Result<String>,
223 ) -> super::Result<String> {
224 let data = match encoding {
225 PaymentEncoding::Win1251 => encoding::all::WINDOWS_1251
226 .decode(bytes, trap)
227 .map_err(|_| super::Error::DecodingError)?,
228 PaymentEncoding::Utf8 => utf8_decode(bytes)?,
229 PaymentEncoding::Koi8R => encoding::all::KOI8_R
230 .decode(bytes, trap)
231 .map_err(|_| super::Error::DecodingError)?,
232 };
233
234 Ok(data)
235 }
236
237 fn validate_required_requisites(&self, requisites: &[Requisite<RT>]) -> super::Result<()> {
238 let mut req = requisites.iter().take(5);
239
240 let next = req.next();
241 if !matches!(next, Some(Requisite::Name(_))) {
242 return Err(super::Error::WrongRequiredRequisiteOrder {
243 passed: next.map(|r| r.key()).unwrap_or("Пусто").into(),
244 expected: "Name".into(),
245 });
246 }
247
248 let next = req.next();
249 if !matches!(next, Some(Requisite::PersonalAcc(_))) {
250 return Err(super::Error::WrongRequiredRequisiteOrder {
251 passed: next.map(|r| r.key()).unwrap_or("Пусто").into(),
252 expected: "PersonalAcc".into(),
253 });
254 }
255
256 let next = req.next();
257 if !matches!(next, Some(Requisite::BankName(_))) {
258 return Err(super::Error::WrongRequiredRequisiteOrder {
259 passed: next.map(|r| r.key()).unwrap_or("Пусто").into(),
260 expected: "BankName".into(),
261 });
262 }
263
264 let next = req.next();
265 if !matches!(next, Some(Requisite::BIC(_))) {
266 return Err(super::Error::WrongRequiredRequisiteOrder {
267 passed: next.map(|r| r.key()).unwrap_or("Пусто").into(),
268 expected: "BIC".into(),
269 });
270 }
271
272 let next = req.next();
273 if !matches!(next, Some(Requisite::CorrespAcc(_))) {
274 return Err(super::Error::WrongRequiredRequisiteOrder {
275 passed: next.map(|r| r.key()).unwrap_or("Пусто").into(),
276 expected: "CorrespAcc".into(),
277 });
278 }
279
280 Ok(())
281 }
282}
283
284impl<T: ParserStrategyType, RT: CustomRequisites> Default for PaymentParser<T, RT> {
285 fn default() -> Self {
286 Self {
287 version_id: VERSION_0001_BYTES,
288 _req_marker: PhantomData,
289 _marker: PhantomData,
290 }
291 }
292}
293
294pub trait ParserStrategyType {}
295
296#[derive(Clone, Copy, Debug, PartialEq, Eq)]
297pub struct StrictParser;
298impl ParserStrategyType for StrictParser {}
299
300#[derive(Clone, Copy, Debug, PartialEq, Eq)]
301pub struct RequisiteToleranceParser;
302impl ParserStrategyType for RequisiteToleranceParser {}
303
304#[derive(Clone, Copy, Debug, PartialEq, Eq)]
305pub struct LooseParser;
306impl ParserStrategyType for LooseParser {}