serial_unit_testing/
utils.rs

1/*
2 * File: src/utils.rs
3 * Date: 30.09.2018
4 * Author: MarkAtk
5 * 
6 * MIT License
7 * 
8 * Copyright (c) 2018 MarkAtk
9 * 
10 * Permission is hereby granted, free of charge, to any person obtaining a copy of
11 * this software and associated documentation files (the "Software"), to deal in
12 * the Software without restriction, including without limitation the rights to
13 * use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
14 * of the Software, and to permit persons to whom the Software is furnished to do
15 * so, subject to the following conditions:
16 * 
17 * The above copyright notice and this permission notice shall be included in all
18 * copies or substantial portions of the Software.
19 * 
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
21 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
22 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
23 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
24 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
25 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
26 * SOFTWARE.
27 */
28
29use std::str;
30use super::error::{Error, Result};
31
32/// Text format type for radix string conversion.
33#[derive(PartialEq, Debug, Copy, Clone)]
34pub enum TextFormat {
35    /// Text format
36    Text,
37    /// Binary format
38    Binary = 2,
39    /// Octal format
40    Octal = 8,
41    /// Decimal format
42    Decimal = 10,
43    /// Hexadecimal format
44    Hex = 16
45}
46
47/// Get a text representation of the text format.
48pub fn get_format_name(format: &TextFormat) -> &str {
49    match format {
50        TextFormat::Text => "Text",
51        TextFormat::Binary => "Binary",
52        TextFormat::Octal => "Octal",
53        TextFormat::Decimal => "Decimal",
54        TextFormat::Hex => "Hexadecimal"
55    }
56}
57
58/// Get the next cyclic text format.
59///
60/// If the last value is passed into this, the first value will be returned.
61pub fn get_next_format(format: &TextFormat) -> TextFormat {
62    match format {
63        TextFormat::Text => TextFormat::Binary,
64        TextFormat::Binary => TextFormat::Octal,
65        TextFormat::Octal => TextFormat::Decimal,
66        TextFormat::Decimal => TextFormat::Hex,
67        TextFormat::Hex => TextFormat::Text
68    }
69}
70
71/// Newline format type
72#[derive(Debug, Copy, Clone, PartialEq)]
73pub enum NewlineFormat {
74    None,
75    CarriageReturn,
76    LineFeed,
77    Both
78}
79
80/// Get a text representation of the newline format.
81pub fn get_newline_format_name(format: &NewlineFormat) -> &str {
82    match format {
83        NewlineFormat::None => "None",
84        NewlineFormat::CarriageReturn => "Carriage return",
85        NewlineFormat::LineFeed => "Line feed",
86        NewlineFormat::Both => "Both"
87    }
88}
89
90/// Get the next cyclic newline format.
91///
92/// If the last value is passed into this, the first value will be returned.
93pub fn get_next_newline_format(format: &NewlineFormat) -> NewlineFormat {
94    match format {
95        NewlineFormat::None => NewlineFormat::CarriageReturn,
96        NewlineFormat::CarriageReturn => NewlineFormat::LineFeed,
97        NewlineFormat::LineFeed => NewlineFormat::Both,
98        NewlineFormat::Both => NewlineFormat::None
99    }
100}
101
102/// Convert a hexadecimal string into a vector of bytes.
103///
104/// Leading 0x and whitespaces will be ignored.
105pub fn bytes_from_hex_string(original_text: &str) -> Result<Vec<u8>> {
106    let mut text = original_text.replace("0x", "");
107    text = text.replace(" ", "");
108
109    bytes_from_radix_string(&text, 16)
110}
111
112/// Convert a binary string into a vector of bytes.
113///
114/// Leading 0b and whitespaces will be ignored.
115pub fn bytes_from_binary_string(original_text: &str) -> Result<Vec<u8>> {
116    let mut text = original_text.replace("0b", "");
117    text = text.replace(" ", "");
118
119    bytes_from_radix_string(&text, 2)
120}
121
122/// Convert a octal string into a vector of bytes
123///
124/// Leading 0 and whitespaces will be ignored.
125pub fn bytes_from_octal_string(original_text: &str) -> Result<Vec<u8>> {
126    let mut text = original_text.replace(" ", "");
127    if text.starts_with('0') {
128        text.remove(0);
129    }
130
131    bytes_from_radix_string(&text, 8)
132}
133
134/// Convert a decimal string into a vector of bytes
135///
136/// Whitespaces will be ignored
137pub fn bytes_from_decimal_string(original_text: &str) -> Result<Vec<u8>> {
138    let text = original_text.replace(" ", "");
139
140    bytes_from_radix_string(&text, 10)
141}
142
143/// Convert a radix string into a vector of bytes.
144///
145/// Leading and trailing whitespaces will result in an error. Conversion happens by 2 characters per byte.
146pub fn bytes_from_radix_string(text: &str, radix: u32) -> Result<Vec<u8>> {
147    let mut bytes: Vec<u8> = Vec::new();
148
149    let mut chars = text.chars().peekable();
150
151    while chars.peek().is_some() {
152        // TODO: Fix single char radix (when count is odd)
153        let chunk: String = chars.by_ref().take(2).collect();
154
155        match u8::from_str_radix(&chunk, radix) {
156            Ok(value) => bytes.push(value),
157            Err(e) => return Err(Error::from(e))
158        };
159    }
160
161    Ok(bytes)
162}
163
164/// Convert a vector of bytes into a radix string with given text format.
165pub fn radix_string(buffer: &[u8], text_format: &TextFormat) -> Result<String> {
166    if *text_format == TextFormat::Text {
167        return match str::from_utf8(buffer) {
168            Ok(text) => Ok(text.to_string()),
169            Err(e) => Err(Error::from(e))
170        };
171    }
172
173    let mut text = String::new();
174
175    for b in buffer {
176        match text_format {
177            TextFormat::Binary => text.push_str(format!("{:08b}", b).as_str()),
178            TextFormat::Octal => text.push_str(format!("{:04o}", b).as_str()),
179            TextFormat::Decimal => text.push_str(format!("{}", b).as_str()),
180            TextFormat::Hex => text.push_str(format!("{:02X}", b).as_str()),
181            _ => ()
182        };
183    }
184
185    Ok(text)
186}
187
188/// Print a vector of bytes in given format.
189///
190/// Depending on the format newlines will be inserted after some entries as followed:
191/// - Binary: 10
192/// - Octal: 16
193/// - Decimal: 18
194/// - Hexadecimal: 20
195///
196/// Row entries will contain the number of entries in the last row after execution.
197pub fn print_radix_string(buffer: &[u8], text_format: &TextFormat, row_entries: &mut u32) {
198    let max_row_entries = match text_format {
199        TextFormat::Binary => 10,
200        TextFormat::Octal => 16,
201        TextFormat::Decimal => 18,
202        TextFormat::Hex => 20,
203        _ => 0
204    };
205
206    for b in buffer {
207        match text_format {
208            TextFormat::Binary => print!("{:#b} ", b),
209            TextFormat::Octal => print!("{:#o} ", b),
210            TextFormat::Hex => print!("0x{:02X} ", b),
211            _ => print!("{}", b)
212        };
213
214        *row_entries += 1;
215        if max_row_entries > 0 && *row_entries > max_row_entries {
216            *row_entries = 0;
217
218            println!();
219        }
220    }
221}
222
223/// Escape given string.
224///
225/// Characters escaped are: \r, \n, \t
226pub fn escape_text(text: String) -> String {
227    let mut text = text.replace("\\r", "\r");
228    text = text.replace("\\n", "\n");
229    text = text.replace("\\t", "\t");
230    text = text.replace("\\\"", "\"");
231
232    text
233}
234
235/// Get actual character count (instead of byte count).
236pub fn char_count(str: &String) -> usize {
237    str.char_indices().count()
238}
239
240/// Get the starting position of a character in a string.
241///
242/// When working with unicode characters a character can span multiple bytes and thus
243/// the offset of characters is not the same as the number of bytes.
244pub fn byte_position_of_char(str: &String, index: usize) -> Option<usize> {
245    match str.char_indices().nth(index) {
246        Some((pos, _)) => Some(pos),
247        None => None
248    }
249}
250
251/// Remove a character by given character index (instead of byte position).
252pub fn remove_char(str: &mut String, index: usize) {
253    if let Some(pos) = byte_position_of_char(str, index) {
254        str.remove(pos);
255    };
256}
257
258/// Insert a character by given character index (instead of byte position).
259pub fn insert_char(str: &mut String, index: usize, c: char) {
260    if let Some(pos) = byte_position_of_char(str, index) {
261        str.insert(pos, c);
262    }
263}
264
265/// Add newline characters to the end of the string.
266///
267/// The newline characters will be added in the given text format.
268pub fn add_newline(text: &mut String, text_format: TextFormat, newline_format: NewlineFormat) {
269    if newline_format == NewlineFormat::None {
270        return;
271    }
272
273    let cr = match text_format {
274        TextFormat::Text => "\r",
275        TextFormat::Binary => "00001101",
276        TextFormat::Octal => "015",
277        TextFormat::Decimal => "13",
278        TextFormat::Hex => "0D"
279    };
280
281    let lf = match text_format {
282        TextFormat::Text => "\n",
283        TextFormat::Binary => "00001010",
284        TextFormat::Octal => "012",
285        TextFormat::Decimal => "10",
286        TextFormat::Hex => "0A"
287    };
288
289    if newline_format == NewlineFormat::CarriageReturn || newline_format == NewlineFormat::Both {
290        text.push_str(cr);
291    }
292
293    if newline_format == NewlineFormat::LineFeed || newline_format == NewlineFormat::Both {
294        text.push_str(lf);
295    }
296}