lib_ruby_parser/source/
decoder.rs

1/// An enum with all possible kinds of errors that can be returned
2/// from a decoder
3#[derive(Debug, PartialEq, Eq, Clone)]
4#[repr(C)]
5pub enum InputError {
6    /// Emitted when no custom decoder provided but input has custom encoding.
7    ///
8    /// You can return this error from your custom decoder if you don't support given encoding.
9    UnsupportedEncoding(String),
10
11    /// Generic error that can be emitted from a custom decoder
12    DecodingError(String),
13}
14
15impl std::fmt::Display for InputError {
16    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
17        write!(f, "{:?}", self)
18    }
19}
20
21impl std::error::Error for InputError {}
22
23/// Result that is returned from decoding function
24#[repr(C)]
25#[derive(Debug, PartialEq, Eq, Clone)]
26pub enum DecoderResult {
27    /// Ok + decoded bytes
28    Ok(Vec<u8>),
29
30    /// Err + reason
31    Err(InputError),
32}
33
34impl DecoderResult {
35    pub(crate) fn into_result(self) -> Result<Vec<u8>, InputError> {
36        match self {
37            Self::Ok(value) => Ok(value),
38            Self::Err(err) => Err(err),
39        }
40    }
41}
42
43/// Decoder is what is used if input source has encoding
44/// that is not supported out of the box.
45///
46/// Supported encoding are:
47/// 1. UTF-8
48/// 2. ASCII-8BIT (or BINARY, it's an alias)
49///
50/// So if your source looks like this:
51///
52/// ```text
53/// # encoding: koi8-r
54/// \xFF = 42
55/// ```
56///
57/// you need to provide a decoder that converts this byte sequence
58/// into UTF-8 bytes.
59///
60/// Decoding function
61///
62/// Takes encoding name and initial input as arguments
63/// and returns `Ok(decoded)` vector of bytes or `Err(error)` that will be returned
64/// in the `ParserResult::diagnostics` vector.
65pub type DecoderFn = dyn Fn(String, Vec<u8>) -> DecoderResult;
66
67/// Custom decoder, a wrapper around a function
68pub struct Decoder {
69    f: Box<DecoderFn>,
70}
71
72impl Decoder {
73    /// Constructs a rewriter based on a given function
74    pub fn new(f: Box<DecoderFn>) -> Self {
75        Self { f }
76    }
77
78    pub(crate) fn call(&self, encoding: String, input: Vec<u8>) -> DecoderResult {
79        let f = &*self.f;
80        f(encoding, input)
81    }
82}
83
84impl std::fmt::Debug for Decoder {
85    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
86        f.debug_struct("Decoder").finish()
87    }
88}
89
90pub fn decode_input(input: Vec<u8>, enc: String, decoder: &mut Option<Decoder>) -> DecoderResult {
91    match enc.to_uppercase().as_str() {
92        "UTF-8" | "ASCII-8BIT" | "BINARY" => DecoderResult::Ok(input),
93        _ => {
94            if let Some(f) = decoder.as_mut() {
95                f.call(enc, input)
96            } else {
97                DecoderResult::Err(InputError::UnsupportedEncoding(enc))
98            }
99        }
100    }
101}