1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147
use std::str::Chars;
/// Traits with extension emthods to wrap strings in bounding characters
pub trait SimpleEnclose {
/// Enclose in a start and an end character with an optional prefix
/// before the main content but after the first character
/// This is a common syntactical pattern in many markup and programming languages
/// the closing character will be the same opening character
/// The optional escape character is inserted before occurrences of the end character
/// unless the preceding character is the escape character itself to avoid double escaping of pre-escaped strings
fn enclose_in_chars(& self, start: char, end: char, prefix: Option<&str>, escape_char: Option<char>) -> String;
/// Enclose in a start and an end character with an optional prefix after the first character
fn enclose_escaped(& self, start: char, end: char, escape_char: Option<char>) -> String {
self.enclose_in_chars(start, end, None, escape_char)
}
/// Enclose in a start and an end character with an optional prefix after the first character
fn enclose(& self, start: char, end: char) -> String {
self.enclose_in_chars(start, end, None, None)
}
/// Enclose in a start and an end character with an optional prefix after the first character
/// escaped where necessary with a backslash \
fn enclose_safe(& self, start: char, end: char) -> String {
self.enclose_in_chars(start, end, None, Some('\\'))
}
/// Wrap a string in a pair of characters, with the closing character matching the first character
/// if it a parenthesis (round bracket), angle bracket, (square) bracket or curly brace. Otherwise
/// the closing character will be the same opening character
/// The optional escape character is inserted before occurrences of the end character
/// unless the preceding character is the escape character itself to avoid double escaping of pre-escaped strings
fn wrap_escaped(& self, opening: char, escape_char: Option<char>) -> String {
let end = match opening {
'(' => ')',
'<' => '>',
'{' => '}',
'[' => ']',
_ => opening
};
self.enclose_in_chars(opening, end, None, escape_char)
}
fn wrap(& self, opening: char) -> String {
let end = match opening {
'(' => ')',
'<' => '>',
'{' => '}',
'[' => ']',
_ => opening
};
self.enclose_in_chars(opening, end, None, None)
}
/// Wrap in matching characters escaped by a backslash \
fn wrap_safe(& self, opening: char) -> String {
let end = match opening {
'(' => ')',
'<' => '>',
'{' => '}',
'[' => ']',
_ => opening
};
self.enclose_in_chars(opening, end, None, Some('\\'))
}
/// wrap in parentheses (sound brackets) with an optional prefix before the main content
fn in_parentheses(& self, prefix: Option<&str>) -> String {
self.enclose_in_chars('(', ')', prefix, None)
}
/// wrap in parentheses (sound brackets)
fn parenthesize(& self) -> String {
self.wrap('(')
}
/// wrap in parentheses (sound brackets)
fn parenthesize_safe(& self) -> String {
self.wrap_safe('(')
}
/// wrap in parentheses (sound brackets)
fn double_quotes(& self) -> String {
self.wrap('"')
}
/// Wrap in single quotes
fn single_quotes(& self) -> String {
self.wrap('\'')
}
/// Wrap in double quotes with escaped quotes in the content
fn double_quotes_safe(& self) -> String {
self.wrap_escaped('"', Some('\\'))
}
/// Wrap in single quotes with escaped quotes in the content
fn single_quotes_safe(& self) -> String {
self.wrap_escaped('\'', Some('\\'))
}
}
/// Implement the base method for &str/String
impl SimpleEnclose for str {
fn enclose_in_chars(&self, start: char, end: char, prefix: Option<&str>, escape_char: Option<char>) -> String {
let mut out = match escape_char {
Some(esc_char) => {
if self.contains(end) {
escape_in_str(self.chars(), end, esc_char)
} else {
self.to_owned()
}
},
_ => self.to_owned()
};
out.insert(0, start);
if let Some(pre) = prefix {
out.insert_str(1, pre);
}
out.push(end);
out
}
}
/// Escape a string enclosed in (double) quotes.
/// The customary escape character is a backslash, but some systems require doubled quote characters
/// To avoid double-escaping, escape characters are not escaped. Strings are thus treated as literals
pub fn escape_in_str(chars: Chars, end: char, esc_char: char) -> String {
let mut new_string = String::new();
let mut prev_char = ' ';
for ch in chars {
if ch == end {
// do not escape escaped characters
if prev_char != esc_char && new_string.len() > 0 {
new_string.push(esc_char);
}
}
new_string.push(ch);
prev_char = ch;
}
new_string
}