1c67d6573Sopenharmony_ciuse std::error;
2c67d6573Sopenharmony_ciuse std::io::{self, Write};
3c67d6573Sopenharmony_ciuse std::process;
4c67d6573Sopenharmony_ciuse std::result;
5c67d6573Sopenharmony_ci
6c67d6573Sopenharmony_ciuse docopt::Docopt;
7c67d6573Sopenharmony_ciuse regex::internal::{Compiler, LiteralSearcher};
8c67d6573Sopenharmony_ciuse regex_syntax::hir::literal::Literals;
9c67d6573Sopenharmony_ciuse regex_syntax::hir::Hir;
10c67d6573Sopenharmony_ci
11c67d6573Sopenharmony_ciconst USAGE: &'static str = "
12c67d6573Sopenharmony_ciUsage:
13c67d6573Sopenharmony_ci    regex-debug [options] ast <pattern>
14c67d6573Sopenharmony_ci    regex-debug [options] hir <pattern>
15c67d6573Sopenharmony_ci    regex-debug [options] prefixes <patterns> ...
16c67d6573Sopenharmony_ci    regex-debug [options] suffixes <patterns> ...
17c67d6573Sopenharmony_ci    regex-debug [options] anchors <pattern>
18c67d6573Sopenharmony_ci    regex-debug [options] captures <pattern>
19c67d6573Sopenharmony_ci    regex-debug [options] compile <patterns> ...
20c67d6573Sopenharmony_ci    regex-debug [options] utf8-ranges <class>
21c67d6573Sopenharmony_ci    regex-debug [options] utf8-ranges-rev <class>
22c67d6573Sopenharmony_ci    regex-debug --help
23c67d6573Sopenharmony_ci
24c67d6573Sopenharmony_ciOptions:
25c67d6573Sopenharmony_ci    --help               Show this usage message.
26c67d6573Sopenharmony_ci    --size-limit ARG     An approximate size limit on the total size (in bytes)
27c67d6573Sopenharmony_ci                         of a compiled regular expression program.
28c67d6573Sopenharmony_ci                         [default: 10485760]
29c67d6573Sopenharmony_ci    --bytes              Show the instruction codes for byte oriented programs.
30c67d6573Sopenharmony_ci                         (As opposed to Unicode oriented programs.)
31c67d6573Sopenharmony_ci    --dfa                Show the instruction codes for a DFA.
32c67d6573Sopenharmony_ci    --dfa-reverse        Show the instruction codes for a reverse DFA.
33c67d6573Sopenharmony_ci                         This implies --dfa.
34c67d6573Sopenharmony_ci    -a, --all-literals   Shows all literals extracted.
35c67d6573Sopenharmony_ci                         By default, only unambiguous literals are shown.
36c67d6573Sopenharmony_ci    --literal-limit ARG  An approximate limit on the total size (in bytes)
37c67d6573Sopenharmony_ci                         of all literals extracted. [default: 250]
38c67d6573Sopenharmony_ci    --class-limit ARG    A limit on the size of character classes used to
39c67d6573Sopenharmony_ci                         extract literals. [default: 10]
40c67d6573Sopenharmony_ci    --literal-bytes      Show raw literal bytes instead of Unicode chars.
41c67d6573Sopenharmony_ci    --lcp                Show the longest common prefix of all the literals
42c67d6573Sopenharmony_ci                         extracted.
43c67d6573Sopenharmony_ci    --lcs                Show the longest common suffix of all the literals
44c67d6573Sopenharmony_ci                         extracted.
45c67d6573Sopenharmony_ci    --searcher           Show the debug output for the literal searcher
46c67d6573Sopenharmony_ci                         constructed by the literals found.
47c67d6573Sopenharmony_ci    --quiet              Show less output.
48c67d6573Sopenharmony_ci";
49c67d6573Sopenharmony_ci
50c67d6573Sopenharmony_ci#[derive(serde::Deserialize)]
51c67d6573Sopenharmony_cistruct Args {
52c67d6573Sopenharmony_ci    cmd_ast: bool,
53c67d6573Sopenharmony_ci    cmd_hir: bool,
54c67d6573Sopenharmony_ci    cmd_prefixes: bool,
55c67d6573Sopenharmony_ci    cmd_suffixes: bool,
56c67d6573Sopenharmony_ci    cmd_anchors: bool,
57c67d6573Sopenharmony_ci    cmd_captures: bool,
58c67d6573Sopenharmony_ci    cmd_compile: bool,
59c67d6573Sopenharmony_ci    cmd_utf8_ranges: bool,
60c67d6573Sopenharmony_ci    cmd_utf8_ranges_rev: bool,
61c67d6573Sopenharmony_ci
62c67d6573Sopenharmony_ci    arg_pattern: String,
63c67d6573Sopenharmony_ci    arg_patterns: Vec<String>,
64c67d6573Sopenharmony_ci    arg_class: String,
65c67d6573Sopenharmony_ci
66c67d6573Sopenharmony_ci    flag_size_limit: usize,
67c67d6573Sopenharmony_ci    flag_bytes: bool,
68c67d6573Sopenharmony_ci    flag_dfa: bool,
69c67d6573Sopenharmony_ci    flag_dfa_reverse: bool,
70c67d6573Sopenharmony_ci    flag_all_literals: bool,
71c67d6573Sopenharmony_ci    flag_literal_limit: usize,
72c67d6573Sopenharmony_ci    flag_class_limit: usize,
73c67d6573Sopenharmony_ci    flag_literal_bytes: bool,
74c67d6573Sopenharmony_ci    flag_lcp: bool,
75c67d6573Sopenharmony_ci    flag_lcs: bool,
76c67d6573Sopenharmony_ci    flag_searcher: bool,
77c67d6573Sopenharmony_ci    flag_quiet: bool,
78c67d6573Sopenharmony_ci}
79c67d6573Sopenharmony_ci
80c67d6573Sopenharmony_citype Result<T> = result::Result<T, Box<dyn error::Error + Send + Sync>>;
81c67d6573Sopenharmony_ci
82c67d6573Sopenharmony_cifn main() {
83c67d6573Sopenharmony_ci    let mut args: Args = Docopt::new(USAGE)
84c67d6573Sopenharmony_ci        .and_then(|d| d.deserialize())
85c67d6573Sopenharmony_ci        .unwrap_or_else(|e| e.exit());
86c67d6573Sopenharmony_ci    if args.flag_dfa_reverse {
87c67d6573Sopenharmony_ci        args.flag_dfa = true;
88c67d6573Sopenharmony_ci    }
89c67d6573Sopenharmony_ci    match run(&args) {
90c67d6573Sopenharmony_ci        Ok(_) => process::exit(0),
91c67d6573Sopenharmony_ci        Err(err) => {
92c67d6573Sopenharmony_ci            let _ = writeln!(&mut io::stderr(), "{}", err);
93c67d6573Sopenharmony_ci            process::exit(1)
94c67d6573Sopenharmony_ci        }
95c67d6573Sopenharmony_ci    }
96c67d6573Sopenharmony_ci}
97c67d6573Sopenharmony_ci
98c67d6573Sopenharmony_cifn run(args: &Args) -> Result<()> {
99c67d6573Sopenharmony_ci    if args.cmd_ast {
100c67d6573Sopenharmony_ci        cmd_ast(args)
101c67d6573Sopenharmony_ci    } else if args.cmd_hir {
102c67d6573Sopenharmony_ci        cmd_hir(args)
103c67d6573Sopenharmony_ci    } else if args.cmd_prefixes {
104c67d6573Sopenharmony_ci        cmd_literals(args)
105c67d6573Sopenharmony_ci    } else if args.cmd_suffixes {
106c67d6573Sopenharmony_ci        cmd_literals(args)
107c67d6573Sopenharmony_ci    } else if args.cmd_anchors {
108c67d6573Sopenharmony_ci        cmd_anchors(args)
109c67d6573Sopenharmony_ci    } else if args.cmd_captures {
110c67d6573Sopenharmony_ci        cmd_captures(args)
111c67d6573Sopenharmony_ci    } else if args.cmd_compile {
112c67d6573Sopenharmony_ci        cmd_compile(args)
113c67d6573Sopenharmony_ci    } else if args.cmd_utf8_ranges {
114c67d6573Sopenharmony_ci        cmd_utf8_ranges(args)
115c67d6573Sopenharmony_ci    } else if args.cmd_utf8_ranges_rev {
116c67d6573Sopenharmony_ci        cmd_utf8_ranges_rev(args)
117c67d6573Sopenharmony_ci    } else {
118c67d6573Sopenharmony_ci        unreachable!()
119c67d6573Sopenharmony_ci    }
120c67d6573Sopenharmony_ci}
121c67d6573Sopenharmony_ci
122c67d6573Sopenharmony_cifn cmd_ast(args: &Args) -> Result<()> {
123c67d6573Sopenharmony_ci    use regex_syntax::ast::parse::Parser;
124c67d6573Sopenharmony_ci
125c67d6573Sopenharmony_ci    let mut parser = Parser::new();
126c67d6573Sopenharmony_ci    let ast = parser.parse(&args.arg_pattern)?;
127c67d6573Sopenharmony_ci    println!("{:#?}", ast);
128c67d6573Sopenharmony_ci    Ok(())
129c67d6573Sopenharmony_ci}
130c67d6573Sopenharmony_ci
131c67d6573Sopenharmony_cifn cmd_hir(args: &Args) -> Result<()> {
132c67d6573Sopenharmony_ci    use regex_syntax::ParserBuilder;
133c67d6573Sopenharmony_ci
134c67d6573Sopenharmony_ci    let mut parser = ParserBuilder::new().allow_invalid_utf8(false).build();
135c67d6573Sopenharmony_ci    let hir = parser.parse(&args.arg_pattern)?;
136c67d6573Sopenharmony_ci    println!("{:#?}", hir);
137c67d6573Sopenharmony_ci    Ok(())
138c67d6573Sopenharmony_ci}
139c67d6573Sopenharmony_ci
140c67d6573Sopenharmony_cifn cmd_literals(args: &Args) -> Result<()> {
141c67d6573Sopenharmony_ci    let exprs = args.parse_many()?;
142c67d6573Sopenharmony_ci    let mut lits = if args.cmd_prefixes {
143c67d6573Sopenharmony_ci        args.literals(&exprs, |lits, e| lits.union_prefixes(e))
144c67d6573Sopenharmony_ci    } else {
145c67d6573Sopenharmony_ci        args.literals(&exprs, |lits, e| lits.union_suffixes(e))
146c67d6573Sopenharmony_ci    };
147c67d6573Sopenharmony_ci    if !args.flag_all_literals {
148c67d6573Sopenharmony_ci        if args.cmd_prefixes {
149c67d6573Sopenharmony_ci            lits = lits.unambiguous_prefixes();
150c67d6573Sopenharmony_ci        } else {
151c67d6573Sopenharmony_ci            lits = lits.unambiguous_suffixes();
152c67d6573Sopenharmony_ci        }
153c67d6573Sopenharmony_ci    }
154c67d6573Sopenharmony_ci    if args.flag_searcher {
155c67d6573Sopenharmony_ci        if args.cmd_prefixes {
156c67d6573Sopenharmony_ci            println!("{:?}", LiteralSearcher::prefixes(lits))
157c67d6573Sopenharmony_ci        } else {
158c67d6573Sopenharmony_ci            println!("{:?}", LiteralSearcher::suffixes(lits))
159c67d6573Sopenharmony_ci        }
160c67d6573Sopenharmony_ci    } else if args.flag_lcp {
161c67d6573Sopenharmony_ci        println!("{}", escape_unicode(lits.longest_common_prefix()));
162c67d6573Sopenharmony_ci    } else if args.flag_lcs {
163c67d6573Sopenharmony_ci        println!("{}", escape_unicode(lits.longest_common_suffix()));
164c67d6573Sopenharmony_ci    } else {
165c67d6573Sopenharmony_ci        for lit in lits.literals() {
166c67d6573Sopenharmony_ci            if args.flag_literal_bytes {
167c67d6573Sopenharmony_ci                if lit.is_cut() {
168c67d6573Sopenharmony_ci                    println!("Cut({})", escape_bytes(lit));
169c67d6573Sopenharmony_ci                } else {
170c67d6573Sopenharmony_ci                    println!("Complete({})", escape_bytes(lit));
171c67d6573Sopenharmony_ci                }
172c67d6573Sopenharmony_ci            } else {
173c67d6573Sopenharmony_ci                println!("{:?}", lit);
174c67d6573Sopenharmony_ci            }
175c67d6573Sopenharmony_ci        }
176c67d6573Sopenharmony_ci    }
177c67d6573Sopenharmony_ci    Ok(())
178c67d6573Sopenharmony_ci}
179c67d6573Sopenharmony_ci
180c67d6573Sopenharmony_cifn cmd_anchors(args: &Args) -> Result<()> {
181c67d6573Sopenharmony_ci    let expr = args.parse_one()?;
182c67d6573Sopenharmony_ci    if expr.is_anchored_start() {
183c67d6573Sopenharmony_ci        println!("start");
184c67d6573Sopenharmony_ci    }
185c67d6573Sopenharmony_ci    if expr.is_anchored_end() {
186c67d6573Sopenharmony_ci        println!("end");
187c67d6573Sopenharmony_ci    }
188c67d6573Sopenharmony_ci    Ok(())
189c67d6573Sopenharmony_ci}
190c67d6573Sopenharmony_ci
191c67d6573Sopenharmony_cifn cmd_captures(args: &Args) -> Result<()> {
192c67d6573Sopenharmony_ci    let expr = args.parse_one()?;
193c67d6573Sopenharmony_ci    let prog = args.compiler().only_utf8(false).compile(&[expr])?;
194c67d6573Sopenharmony_ci    for (i, name) in prog.captures.iter().enumerate() {
195c67d6573Sopenharmony_ci        match *name {
196c67d6573Sopenharmony_ci            None => println!("{}", i),
197c67d6573Sopenharmony_ci            Some(ref name) => println!("{}:{}", i, name),
198c67d6573Sopenharmony_ci        }
199c67d6573Sopenharmony_ci    }
200c67d6573Sopenharmony_ci    Ok(())
201c67d6573Sopenharmony_ci}
202c67d6573Sopenharmony_ci
203c67d6573Sopenharmony_cifn cmd_compile(args: &Args) -> Result<()> {
204c67d6573Sopenharmony_ci    let exprs = args.parse_many()?;
205c67d6573Sopenharmony_ci    let compiler = args
206c67d6573Sopenharmony_ci        .compiler()
207c67d6573Sopenharmony_ci        .bytes(args.flag_bytes)
208c67d6573Sopenharmony_ci        .only_utf8(!args.flag_bytes)
209c67d6573Sopenharmony_ci        .dfa(args.flag_dfa)
210c67d6573Sopenharmony_ci        .reverse(args.flag_dfa_reverse);
211c67d6573Sopenharmony_ci    let prog = compiler.compile(&exprs)?;
212c67d6573Sopenharmony_ci    if !args.flag_quiet {
213c67d6573Sopenharmony_ci        print!("{:?}", prog);
214c67d6573Sopenharmony_ci    } else {
215c67d6573Sopenharmony_ci        println!("instruction count: {}", prog.insts.len());
216c67d6573Sopenharmony_ci    }
217c67d6573Sopenharmony_ci    Ok(())
218c67d6573Sopenharmony_ci}
219c67d6573Sopenharmony_ci
220c67d6573Sopenharmony_cifn cmd_utf8_ranges(args: &Args) -> Result<()> {
221c67d6573Sopenharmony_ci    use regex_syntax::hir::{self, HirKind};
222c67d6573Sopenharmony_ci    use regex_syntax::utf8::Utf8Sequences;
223c67d6573Sopenharmony_ci    use regex_syntax::ParserBuilder;
224c67d6573Sopenharmony_ci
225c67d6573Sopenharmony_ci    let hir = ParserBuilder::new()
226c67d6573Sopenharmony_ci        .build()
227c67d6573Sopenharmony_ci        .parse(&format!("[{}]", args.arg_class))?;
228c67d6573Sopenharmony_ci    let cls = match hir.into_kind() {
229c67d6573Sopenharmony_ci        HirKind::Class(hir::Class::Unicode(cls)) => cls,
230c67d6573Sopenharmony_ci        _ => {
231c67d6573Sopenharmony_ci            return Err(
232c67d6573Sopenharmony_ci                format!("unexpected HIR, expected Unicode class").into()
233c67d6573Sopenharmony_ci            )
234c67d6573Sopenharmony_ci        }
235c67d6573Sopenharmony_ci    };
236c67d6573Sopenharmony_ci    let mut char_count = 0;
237c67d6573Sopenharmony_ci    for (i, range) in cls.iter().enumerate() {
238c67d6573Sopenharmony_ci        if i > 0 {
239c67d6573Sopenharmony_ci            println!("----------------------------");
240c67d6573Sopenharmony_ci        }
241c67d6573Sopenharmony_ci        char_count += (range.end() as u32) - (range.start() as u32) + 1;
242c67d6573Sopenharmony_ci        for seq in Utf8Sequences::new(range.start(), range.end()) {
243c67d6573Sopenharmony_ci            for utf8_range in seq.into_iter() {
244c67d6573Sopenharmony_ci                print!("[{:02X}-{:02X}]", utf8_range.start, utf8_range.end);
245c67d6573Sopenharmony_ci            }
246c67d6573Sopenharmony_ci            println!();
247c67d6573Sopenharmony_ci        }
248c67d6573Sopenharmony_ci    }
249c67d6573Sopenharmony_ci    println!("codepoint count: {}", char_count);
250c67d6573Sopenharmony_ci    Ok(())
251c67d6573Sopenharmony_ci}
252c67d6573Sopenharmony_ci
253c67d6573Sopenharmony_cifn cmd_utf8_ranges_rev(args: &Args) -> Result<()> {
254c67d6573Sopenharmony_ci    use regex_syntax::hir::{self, HirKind};
255c67d6573Sopenharmony_ci    use regex_syntax::utf8::Utf8Sequences;
256c67d6573Sopenharmony_ci    use regex_syntax::ParserBuilder;
257c67d6573Sopenharmony_ci
258c67d6573Sopenharmony_ci    let hir = ParserBuilder::new()
259c67d6573Sopenharmony_ci        .build()
260c67d6573Sopenharmony_ci        .parse(&format!("[{}]", args.arg_class))?;
261c67d6573Sopenharmony_ci    let cls = match hir.into_kind() {
262c67d6573Sopenharmony_ci        HirKind::Class(hir::Class::Unicode(cls)) => cls,
263c67d6573Sopenharmony_ci        _ => {
264c67d6573Sopenharmony_ci            return Err(
265c67d6573Sopenharmony_ci                format!("unexpected HIR, expected Unicode class").into()
266c67d6573Sopenharmony_ci            )
267c67d6573Sopenharmony_ci        }
268c67d6573Sopenharmony_ci    };
269c67d6573Sopenharmony_ci    let mut char_count = 0;
270c67d6573Sopenharmony_ci    let mut seqs = vec![];
271c67d6573Sopenharmony_ci    for (_, range) in cls.iter().enumerate() {
272c67d6573Sopenharmony_ci        char_count += (range.end() as u32) - (range.start() as u32) + 1;
273c67d6573Sopenharmony_ci        for seq in Utf8Sequences::new(range.start(), range.end()) {
274c67d6573Sopenharmony_ci            let mut seq = seq.as_slice().to_vec();
275c67d6573Sopenharmony_ci            seq.reverse();
276c67d6573Sopenharmony_ci            seqs.push(seq);
277c67d6573Sopenharmony_ci        }
278c67d6573Sopenharmony_ci    }
279c67d6573Sopenharmony_ci    seqs.sort();
280c67d6573Sopenharmony_ci    for seq in seqs {
281c67d6573Sopenharmony_ci        for utf8_range in seq.into_iter() {
282c67d6573Sopenharmony_ci            print!("[{:02X}-{:02X}]", utf8_range.start, utf8_range.end);
283c67d6573Sopenharmony_ci        }
284c67d6573Sopenharmony_ci        println!();
285c67d6573Sopenharmony_ci    }
286c67d6573Sopenharmony_ci    println!("codepoint count: {}", char_count);
287c67d6573Sopenharmony_ci    Ok(())
288c67d6573Sopenharmony_ci}
289c67d6573Sopenharmony_ci
290c67d6573Sopenharmony_ciimpl Args {
291c67d6573Sopenharmony_ci    fn parse_one(&self) -> Result<Hir> {
292c67d6573Sopenharmony_ci        parse(&self.arg_pattern)
293c67d6573Sopenharmony_ci    }
294c67d6573Sopenharmony_ci
295c67d6573Sopenharmony_ci    fn parse_many(&self) -> Result<Vec<Hir>> {
296c67d6573Sopenharmony_ci        self.arg_patterns.iter().map(|s| parse(s)).collect()
297c67d6573Sopenharmony_ci    }
298c67d6573Sopenharmony_ci
299c67d6573Sopenharmony_ci    fn literals<F: Fn(&mut Literals, &Hir) -> bool>(
300c67d6573Sopenharmony_ci        &self,
301c67d6573Sopenharmony_ci        exprs: &[Hir],
302c67d6573Sopenharmony_ci        get_literals: F,
303c67d6573Sopenharmony_ci    ) -> Literals {
304c67d6573Sopenharmony_ci        let mut lits = Some(self.empty_literals());
305c67d6573Sopenharmony_ci        for e in exprs {
306c67d6573Sopenharmony_ci            lits = lits.and_then(|mut lits| {
307c67d6573Sopenharmony_ci                if !get_literals(&mut lits, e) {
308c67d6573Sopenharmony_ci                    None
309c67d6573Sopenharmony_ci                } else {
310c67d6573Sopenharmony_ci                    Some(lits)
311c67d6573Sopenharmony_ci                }
312c67d6573Sopenharmony_ci            });
313c67d6573Sopenharmony_ci        }
314c67d6573Sopenharmony_ci        lits.unwrap_or(self.empty_literals())
315c67d6573Sopenharmony_ci    }
316c67d6573Sopenharmony_ci
317c67d6573Sopenharmony_ci    fn empty_literals(&self) -> Literals {
318c67d6573Sopenharmony_ci        let mut lits = Literals::empty();
319c67d6573Sopenharmony_ci        lits.set_limit_size(self.flag_literal_limit);
320c67d6573Sopenharmony_ci        lits.set_limit_class(self.flag_class_limit);
321c67d6573Sopenharmony_ci        lits
322c67d6573Sopenharmony_ci    }
323c67d6573Sopenharmony_ci
324c67d6573Sopenharmony_ci    fn compiler(&self) -> Compiler {
325c67d6573Sopenharmony_ci        Compiler::new().size_limit(self.flag_size_limit)
326c67d6573Sopenharmony_ci    }
327c67d6573Sopenharmony_ci}
328c67d6573Sopenharmony_ci
329c67d6573Sopenharmony_cifn parse(re: &str) -> Result<Hir> {
330c67d6573Sopenharmony_ci    use regex_syntax::ParserBuilder;
331c67d6573Sopenharmony_ci    ParserBuilder::new()
332c67d6573Sopenharmony_ci        .allow_invalid_utf8(true)
333c67d6573Sopenharmony_ci        .build()
334c67d6573Sopenharmony_ci        .parse(re)
335c67d6573Sopenharmony_ci        .map_err(From::from)
336c67d6573Sopenharmony_ci}
337c67d6573Sopenharmony_ci
338c67d6573Sopenharmony_cifn escape_unicode(bytes: &[u8]) -> String {
339c67d6573Sopenharmony_ci    let show = match ::std::str::from_utf8(bytes) {
340c67d6573Sopenharmony_ci        Ok(v) => v.to_string(),
341c67d6573Sopenharmony_ci        Err(_) => escape_bytes(bytes),
342c67d6573Sopenharmony_ci    };
343c67d6573Sopenharmony_ci    let mut space_escaped = String::new();
344c67d6573Sopenharmony_ci    for c in show.chars() {
345c67d6573Sopenharmony_ci        if c.is_whitespace() {
346c67d6573Sopenharmony_ci            let escaped = if c as u32 <= 0x7F {
347c67d6573Sopenharmony_ci                escape_byte(c as u8)
348c67d6573Sopenharmony_ci            } else {
349c67d6573Sopenharmony_ci                if c as u32 <= 0xFFFF {
350c67d6573Sopenharmony_ci                    format!(r"\u{{{:04x}}}", c as u32)
351c67d6573Sopenharmony_ci                } else {
352c67d6573Sopenharmony_ci                    format!(r"\U{{{:08x}}}", c as u32)
353c67d6573Sopenharmony_ci                }
354c67d6573Sopenharmony_ci            };
355c67d6573Sopenharmony_ci            space_escaped.push_str(&escaped);
356c67d6573Sopenharmony_ci        } else {
357c67d6573Sopenharmony_ci            space_escaped.push(c);
358c67d6573Sopenharmony_ci        }
359c67d6573Sopenharmony_ci    }
360c67d6573Sopenharmony_ci    space_escaped
361c67d6573Sopenharmony_ci}
362c67d6573Sopenharmony_ci
363c67d6573Sopenharmony_cifn escape_bytes(bytes: &[u8]) -> String {
364c67d6573Sopenharmony_ci    let mut s = String::new();
365c67d6573Sopenharmony_ci    for &b in bytes {
366c67d6573Sopenharmony_ci        s.push_str(&escape_byte(b));
367c67d6573Sopenharmony_ci    }
368c67d6573Sopenharmony_ci    s
369c67d6573Sopenharmony_ci}
370c67d6573Sopenharmony_ci
371c67d6573Sopenharmony_cifn escape_byte(byte: u8) -> String {
372c67d6573Sopenharmony_ci    use std::ascii::escape_default;
373c67d6573Sopenharmony_ci
374c67d6573Sopenharmony_ci    let escaped: Vec<u8> = escape_default(byte).collect();
375c67d6573Sopenharmony_ci    String::from_utf8_lossy(&escaped).into_owned()
376c67d6573Sopenharmony_ci}
377