yatima_core/parse/
base.rs

1use crate::parse::error::{
2  throw_err,
3  ParseError,
4  ParseErrorKind,
5};
6
7use crate::parse::span::Span;
8
9use multibase::Base;
10
11use nom::{
12  branch::alt,
13  bytes::complete::{
14    tag,
15    take_till,
16    take_till1,
17  },
18  character::complete::satisfy,
19  combinator::value,
20  error::context,
21  IResult,
22  InputTakeAtPosition,
23};
24
25use sp_std::{
26  borrow::ToOwned,
27  boxed::Box,
28  vec::Vec,
29};
30
31use alloc::string::String;
32
33#[derive(PartialEq, Eq, Clone, Copy, Debug)]
34pub enum LitBase {
35  Bin,
36  Oct,
37  Dec,
38  Hex,
39}
40
41impl Default for LitBase {
42  fn default() -> Self { Self::Hex }
43}
44
45impl LitBase {
46  pub fn parse_code(i: Span) -> IResult<Span, Self, ParseError<Span>> {
47    alt((
48      value(Self::Bin, tag("b")),
49      value(Self::Oct, tag("o")),
50      value(Self::Dec, tag("d")),
51      value(Self::Hex, tag("x")),
52    ))(i)
53  }
54
55  /// Get the code corresponding to the base algorithm.
56  pub fn code(&self) -> char {
57    match self {
58      Self::Bin => 'b',
59      Self::Oct => 'o',
60      Self::Dec => 'd',
61      Self::Hex => 'x',
62    }
63  }
64
65  pub fn base_digits(&self) -> &str {
66    match self {
67      Self::Bin => "01",
68      Self::Oct => "01234567",
69      Self::Dec => "0123456789",
70      Self::Hex => "0123456789abcdef",
71    }
72  }
73
74  pub fn radix(&self) -> u32 {
75    match self {
76      Self::Bin => 2,
77      Self::Oct => 8,
78      Self::Dec => 10,
79      Self::Hex => 16,
80    }
81  }
82
83  pub fn is_digit(&self, x: char) -> bool {
84    self.base_digits().chars().any(|y| x == y)
85  }
86
87  pub fn encode<I: AsRef<[u8]>>(&self, input: I) -> String {
88    base_x::encode(self.base_digits(), input.as_ref())
89  }
90
91  pub fn decode<'a>(
92    &self,
93    input: Span<'a>,
94  ) -> IResult<Span<'a>, Vec<u8>, ParseError<Span<'a>>> {
95    let (i, o) = input.split_at_position_complete(|x| !self.is_digit(x))?;
96    match base_x::decode(self.base_digits(), o.fragment()) {
97      Ok(bytes) => Ok((i, bytes)),
98      Err(_) => Err(nom::Err::Error(ParseError::new(
99        i,
100        ParseErrorKind::InvalidBaseEncoding(*self),
101      ))),
102    }
103  }
104
105  pub fn decode1<'a>(
106    &self,
107    input: Span<'a>,
108  ) -> IResult<Span<'a>, Vec<u8>, ParseError<Span<'a>>> {
109    let (i, o) = input.split_at_position1_complete(
110      |x| !self.is_digit(x),
111      nom::error::ErrorKind::Digit,
112    )?;
113    match base_x::decode(self.base_digits(), o.fragment()) {
114      Ok(bytes) => Ok((i, bytes)),
115      Err(_) => Err(nom::Err::Error(ParseError::new(
116        i,
117        ParseErrorKind::InvalidBaseEncoding(*self),
118      ))),
119    }
120  }
121}
122
123pub fn parse_bin_digits()
124-> impl Fn(Span) -> IResult<Span, String, ParseError<Span>> {
125  move |from: Span| {
126    let (i, d) =
127      context("binary digit", satisfy(|x| LitBase::Bin.is_digit(x)))(from)?;
128    let (i, ds) = context(
129      "binary digits",
130      take_till(|x| !(LitBase::Bin.is_digit(x) || x == '_')),
131    )(i)?;
132    let ds: String = sp_std::iter::once(d)
133      .chain((*ds.fragment()).to_owned().chars())
134      .filter(|x| *x != '_')
135      .collect();
136    Ok((i, ds))
137  }
138}
139
140pub fn parse_oct_digits()
141-> impl Fn(Span) -> IResult<Span, String, ParseError<Span>> {
142  move |from: Span| {
143    let (i, d) =
144      context("octal digit", satisfy(|x| LitBase::Oct.is_digit(x)))(from)?;
145    let (i, ds) = context(
146      "octal digits",
147      take_till(|x| !(LitBase::Oct.is_digit(x) || x == '_')),
148    )(i)?;
149    let ds: String = sp_std::iter::once(d)
150      .chain((*ds.fragment()).to_owned().chars())
151      .filter(|x| *x != '_')
152      .collect();
153    Ok((i, ds))
154  }
155}
156
157pub fn parse_dec_digits()
158-> impl Fn(Span) -> IResult<Span, String, ParseError<Span>> {
159  move |from: Span| {
160    let (i, d) =
161      context("decimal digit", satisfy(|x| LitBase::Dec.is_digit(x)))(from)?;
162    let (i, ds) = context(
163      "decimal digits",
164      take_till(|x| !(LitBase::Dec.is_digit(x) || x == '_')),
165    )(i)?;
166    let ds: String = sp_std::iter::once(d)
167      .chain((*ds.fragment()).to_owned().chars())
168      .filter(|x| *x != '_')
169      .collect();
170    Ok((i, ds))
171  }
172}
173
174pub fn parse_hex_digits()
175-> impl Fn(Span) -> IResult<Span, String, ParseError<Span>> {
176  move |from: Span| {
177    let (i, d) = context(
178      "hexadecimal digit",
179      satisfy(|x| LitBase::Hex.is_digit(x)),
180    )(from)?;
181    let (i, ds) = context(
182      "hexadecimal digits",
183      take_till(|x| !(LitBase::Hex.is_digit(x) || x == '_')),
184    )(i)?;
185    let ds: String = sp_std::iter::once(d)
186      .chain((*ds.fragment()).to_owned().chars())
187      .filter(|x| *x != '_')
188      .collect();
189    Ok((i, ds))
190  }
191}
192
193pub fn parse_litbase_code()
194-> impl Fn(Span) -> IResult<Span, LitBase, ParseError<Span>> {
195  move |from: Span| {
196    throw_err(
197      alt((
198        value(LitBase::Bin, tag("b")),
199        value(LitBase::Oct, tag("o")),
200        value(LitBase::Dec, tag("d")),
201        value(LitBase::Hex, tag("x")),
202      ))(from),
203      |_| ParseError::new(from, ParseErrorKind::UnknownBaseCode),
204    )
205  }
206}
207pub fn parse_litbase_bits_code()
208-> impl Fn(Span) -> IResult<Span, LitBase, ParseError<Span>> {
209  move |from: Span| {
210    throw_err(
211      alt((value(LitBase::Bin, tag("b")), value(LitBase::Hex, tag("x"))))(from),
212      |_| ParseError::new(from, ParseErrorKind::UnknownBaseCode),
213    )
214  }
215}
216
217pub fn parse_litbase_digits(
218  base: LitBase,
219) -> Box<dyn Fn(Span) -> IResult<Span, String, ParseError<Span>>> {
220  Box::new(move |from: Span| match base {
221    LitBase::Bin => parse_bin_digits()(from),
222    LitBase::Oct => parse_oct_digits()(from),
223    LitBase::Dec => parse_dec_digits()(from),
224    LitBase::Hex => parse_hex_digits()(from),
225  })
226}
227
228pub fn parse_litbase_bytes(
229  base: LitBase,
230) -> impl Fn(Span) -> IResult<Span, Vec<u8>, ParseError<Span>> {
231  move |from: Span| {
232    let (i, o) = parse_litbase_digits(base)(from)?;
233    match base_x::decode(base.base_digits(), &o) {
234      Ok(bytes) => Ok((i, bytes)),
235      Err(_) => Err(nom::Err::Error(ParseError::new(
236        i,
237        ParseErrorKind::InvalidBaseEncoding(base),
238      ))),
239    }
240  }
241}
242
243pub const MULTIBASE_DIGITS: &str =
244  "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/-_";
245
246pub fn is_multibase_digit(x: char) -> bool {
247  MULTIBASE_DIGITS.chars().any(|y| x == y)
248}
249
250pub fn parse_multibase_digits()
251-> impl Fn(Span) -> IResult<Span, Span, ParseError<Span>> {
252  move |from: Span| {
253    context("multibase digits", take_till1(|x| !is_multibase_digit(x)))(from)
254  }
255}
256
257pub fn parse_multibase()
258-> impl Fn(Span) -> IResult<Span, (Base, Vec<u8>), ParseError<Span>> {
259  move |from: Span| {
260    let (i, o) = parse_multibase_digits()(from)?;
261    match multibase::decode(o.fragment()) {
262      Ok((base, bytes)) => Ok((i, (base, bytes))),
263      Err(e) => Err(nom::Err::Error(ParseError::new(
264        i,
265        ParseErrorKind::MultibaseError(e),
266      ))),
267    }
268  }
269}