1
- use crate :: helpers:: { get_var_regex, get_var_regex_bytes, quotable_contains , quotable_into_string} ;
1
+ use crate :: helpers:: { get_var_regex, get_var_regex_bytes, quotable_into_string} ;
2
2
use shell_quote:: { Bash , QuoteRefExt } ;
3
3
use std:: sync:: Arc ;
4
4
5
5
pub use shell_quote:: Quotable ;
6
6
7
- fn string_vec < T : AsRef < str > > ( items : & [ T ] ) -> Vec < String > {
8
- items
9
- . iter ( )
10
- . map ( |item| item. as_ref ( ) . to_string ( ) )
11
- . collect :: < Vec < _ > > ( )
12
- }
13
-
14
7
fn quote ( data : Quotable < ' _ > ) -> String {
15
8
data. quoted ( Bash )
16
9
}
@@ -31,16 +24,22 @@ fn quote_expansion(data: Quotable<'_>) -> String {
31
24
output
32
25
}
33
26
27
+ /// Types of syntax to check for to determine quoting.
28
+ pub enum Syntax {
29
+ Symbol ( String ) ,
30
+ Pair ( String , String ) ,
31
+ }
32
+
34
33
/// Options for [`Quoter`].
35
34
pub struct QuoterOptions {
36
35
/// List of start and end quotes for strings.
37
36
pub quote_pairs : Vec < ( String , String ) > ,
38
37
39
38
/// List of syntax and characters that must be quoted for expansion.
40
- pub quoted_syntax : Vec < String > ,
39
+ pub quoted_syntax : Vec < Syntax > ,
41
40
42
41
/// List of syntax and characters that must not be quoted.
43
- pub unquoted_syntax : Vec < String > ,
42
+ pub unquoted_syntax : Vec < Syntax > ,
44
43
45
44
/// Handler to apply quoting.
46
45
pub on_quote : Arc < dyn Fn ( Quotable < ' _ > ) -> String > ,
@@ -54,15 +53,31 @@ impl Default for QuoterOptions {
54
53
Self {
55
54
quote_pairs : vec ! [ ( "'" . into( ) , "'" . into( ) ) , ( "\" " . into( ) , "\" " . into( ) ) ] ,
56
55
// https://www.gnu.org/software/bash/manual/bash.html#Shell-Expansions
57
- quoted_syntax : string_vec ( & [
58
- "${" , // param
59
- "$(" , // command
60
- ] ) ,
61
- unquoted_syntax : string_vec ( & [
62
- "{" , "}" , // brace
63
- "<(" , ">(" , // process
64
- "**" , "*" , "?" , "?(" , "*(" , "+(" , "@(" , "!(" , // file, glob
65
- ] ) ,
56
+ quoted_syntax : vec ! [
57
+ // param
58
+ Syntax :: Pair ( "${" . into( ) , "}" . into( ) ) ,
59
+ // command
60
+ Syntax :: Pair ( "$(" . into( ) , ")" . into( ) ) ,
61
+ // arithmetic
62
+ Syntax :: Pair ( "$((" . into( ) , "))" . into( ) ) ,
63
+ ] ,
64
+ unquoted_syntax : vec ! [
65
+ // brace
66
+ Syntax :: Pair ( "{" . into( ) , "}" . into( ) ) ,
67
+ // process
68
+ Syntax :: Pair ( "<(" . into( ) , ")" . into( ) ) ,
69
+ Syntax :: Pair ( ">(" . into( ) , ")" . into( ) ) ,
70
+ // file, glob
71
+ Syntax :: Symbol ( "**" . into( ) ) ,
72
+ Syntax :: Symbol ( "*" . into( ) ) ,
73
+ Syntax :: Symbol ( "?" . into( ) ) ,
74
+ Syntax :: Pair ( "[" . into( ) , "]" . into( ) ) ,
75
+ Syntax :: Pair ( "?(" . into( ) , ")" . into( ) ) ,
76
+ Syntax :: Pair ( "*(" . into( ) , ")" . into( ) ) ,
77
+ Syntax :: Pair ( "+(" . into( ) , ")" . into( ) ) ,
78
+ Syntax :: Pair ( "@(" . into( ) , ")" . into( ) ) ,
79
+ Syntax :: Pair ( "!(" . into( ) , ")" . into( ) ) ,
80
+ ] ,
66
81
on_quote : Arc :: new ( quote) ,
67
82
on_quote_expansion : Arc :: new ( quote_expansion) ,
68
83
}
@@ -151,7 +166,7 @@ impl<'a> Quoter<'a> {
151
166
152
167
/// Return true if the provided string requires expansion.
153
168
pub fn requires_expansion ( & self ) -> bool {
154
- if quotable_contains ( & self . data , & self . options . quoted_syntax ) {
169
+ if quotable_contains_syntax ( & self . data , & self . options . quoted_syntax ) {
155
170
return true ;
156
171
}
157
172
@@ -163,6 +178,58 @@ impl<'a> Quoter<'a> {
163
178
164
179
/// Return true if the provided string must be unquoted.
165
180
pub fn requires_unquoted ( & self ) -> bool {
166
- quotable_contains ( & self . data , & self . options . unquoted_syntax )
181
+ quotable_contains_syntax ( & self . data , & self . options . unquoted_syntax )
182
+ }
183
+ }
184
+
185
+ fn quotable_contains_syntax ( data : & Quotable < ' _ > , syntaxes : & [ Syntax ] ) -> bool {
186
+ for syntax in syntaxes {
187
+ match data {
188
+ Quotable :: Bytes ( bytes) => {
189
+ match syntax {
190
+ Syntax :: Symbol ( symbol) => {
191
+ let sbytes = symbol. as_bytes ( ) ;
192
+
193
+ if bytes. windows ( sbytes. len ( ) ) . any ( |chunk| chunk == sbytes) {
194
+ return true ;
195
+ }
196
+ }
197
+ Syntax :: Pair ( open, close) => {
198
+ let obytes = open. as_bytes ( ) ;
199
+ let cbytes = close. as_bytes ( ) ;
200
+
201
+ if let Some ( o) = bytes
202
+ . windows ( obytes. len ( ) )
203
+ . position ( |chunk| chunk == obytes)
204
+ {
205
+ if bytes[ o..]
206
+ . windows ( cbytes. len ( ) )
207
+ . any ( |chunk| chunk == cbytes)
208
+ {
209
+ return true ;
210
+ }
211
+ }
212
+ }
213
+ } ;
214
+ }
215
+ Quotable :: Text ( text) => {
216
+ match syntax {
217
+ Syntax :: Symbol ( symbol) => {
218
+ if text. contains ( symbol) {
219
+ return true ;
220
+ }
221
+ }
222
+ Syntax :: Pair ( open, close) => {
223
+ if let Some ( o) = text. find ( open) {
224
+ if text[ o..] . contains ( close) {
225
+ return true ;
226
+ }
227
+ }
228
+ }
229
+ } ;
230
+ }
231
+ } ;
167
232
}
233
+
234
+ false
168
235
}
0 commit comments