gost_56042/
parser.rs

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
15/// Интерфейс для парсеров.
16pub trait ParserStrategy<T: CustomRequisites> {
17    /// Преобразовать из строки.
18    ///
19    /// Предполагается, что тело находится в Utf-8 формате.
20    fn parse_from_str(&self, val: &str) -> super::Result<Payment<T>>;
21
22    /// Преобразование из байтов.
23    fn parse_from_bytes(&self, bytes: &[u8]) -> super::Result<Payment<T>>;
24}
25
26/// Парсер из строки в структуру с информацией о платеже.
27#[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    /// Установка версии.
39    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 {}