simple_string_patterns/
enclose.rs

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