pmd_code_table/
text_to_code.rs

1use std::{collections::HashMap, num::ParseIntError};
2
3use thiserror::Error;
4
5use crate::CodeTableEntryFile;
6
7#[derive(Debug, Error)]
8pub enum TextToCodeError {
9    #[error("The character '{0:?}' has been escaped, but it doesn't need to. If you want to display \\, you need to write \\\\.")]
10    UselessEscape(char),
11    #[error("The final character of the string is an unescaped \\. If you want to display \\, add another \\ at the end of that string.")]
12    UnfinishedEscape,
13    #[error("The string end with an unfinished placeholder. [ start an escape sequence if they aren't preceded with \\ (this \\ isn't displayed), and a ] close it.")]
14    UnclosedPlaceholder,
15    #[error("The string contain an empty placeholder []. If you want to display [], write \\[] instead (to escape the [)")]
16    EmptyPlaceholder,
17    #[error("The string contain a placeholder containing too much part (a part in a placeholder is separated with :). The various part of the placeholder are : {0:?}")]
18    PlaceholderTooMuchPart(Vec<String>),
19    #[error("The placeholder {0:?} is unrecognized")]
20    UnknownPlaceholder(String),
21    #[error("The value \"{1:?}\" for the placeholder \"{2:?}\" is neither an hardcoded one, nor a base 10 string (or it may be a base 10 number, but superior to 2^32 - 1)")]
22    InvalidValue(#[source] ParseIntError, String, String),
23    #[error("The placeholder {1:?} has the associated value {0:?}, thought it should less or equal to 255 (hard value for this placeholder type")]
24    CantEncodedParameterEmbeddedData(u32, String)
25}
26
27pub struct TextToCode<'a> {
28    pub(crate) text_to_code: HashMap<&'a String, &'a CodeTableEntryFile>,
29}
30
31impl<'a> TextToCode<'a> {
32    pub fn encode(&self, text: &str) -> Result<Vec<u16>, TextToCodeError> {
33        let mut buffer = [0; 2];
34        let mut result: Vec<u16> = Vec::new();
35
36        let mut iterator = text.chars();
37        while let Some(chara) = iterator.next() {
38            if chara == '[' {
39                let mut placeholder = Vec::new();
40                let mut placeholder_current_string = String::with_capacity(10);
41                loop {
42                    if let Some(placeholder_char) = iterator.next() {
43                        if placeholder_char == ']' {
44                            placeholder.push(placeholder_current_string.clone());
45                            break;
46                        } else if placeholder_char == ':' {
47                            placeholder_current_string.push(':');
48                            placeholder.push(placeholder_current_string.clone());
49                            placeholder_current_string = String::with_capacity(10);
50                        } else {
51                            placeholder_current_string.push(placeholder_char)
52                        }
53                    } else {
54                        return Err(TextToCodeError::UnclosedPlaceholder);
55                    }
56                }
57                let (directive, value) = match placeholder.len() {
58                    0 => return Err(TextToCodeError::EmptyPlaceholder),
59                    1 => (placeholder[0].clone(), None),
60                    2 => (placeholder[0].clone(), Some(placeholder[1].clone())),
61                    _ => return Err(TextToCodeError::PlaceholderTooMuchPart(placeholder)),
62                };
63
64                let complete_placeholder = if let Some(ref value) = value {
65                    format!("{}{}", directive, value)
66                } else {
67                    directive.clone()
68                };
69
70                if let Some(placeholder_char) = self.text_to_code.get(&complete_placeholder) {
71                    result.push(placeholder_char.value);
72                } else if let Some(value) = value.clone() {
73                    let entry = *self.text_to_code.get(&directive).map_or_else(|| Err(TextToCodeError::UnknownPlaceholder(directive.clone())), Ok)?;
74                    let value_number = u32::from_str_radix(&value, 10).map_err(|err| TextToCodeError::InvalidValue(err, value.clone(), directive.clone()))?;
75                    if entry.lenght == 0 {
76                        if value_number > 255 {
77                            return Err(TextToCodeError::CantEncodedParameterEmbeddedData(value_number, entry.string.clone()))
78                        };
79                        result.push(entry.value + value_number as u16);
80                    } else {
81                        result.push(entry.value + (value_number % 256).saturating_sub(1) as u16);
82                        let mut remaining = value_number;
83                        loop {
84                            if remaining == 0 {
85                                break
86                            }
87                            let this_part = remaining & 0x0000FFFF;
88                            remaining = remaining >> 16;
89                            result.push(this_part as u16);
90                        }
91                    }
92                } else {
93                    return Err(TextToCodeError::UnknownPlaceholder(complete_placeholder))
94                }
95
96            } else if chara == '\\' {
97                if let Some(next_chara) = iterator.next() {
98                    if next_chara == '[' || next_chara == '\\' {
99                        let slice = next_chara.encode_utf16(&mut buffer);
100                        result.extend(slice.iter());
101                    } else {
102                        return Err(TextToCodeError::UselessEscape(next_chara));
103                    }
104                } else {
105                    return Err(TextToCodeError::UnfinishedEscape);
106                }
107            } else {
108                let slice = chara.encode_utf16(&mut buffer);
109                result.extend(slice.iter());
110            }
111        }
112
113        Ok(result)
114    }
115}