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