Update formatter to rustfmt 2.0
[hiphop-php.git] / hphp / hack / src / hhbc / string_utils.rs
blob1dec6328552346d66e620c0e02aeac440b0635de
1 // Copyright (c) Facebook, Inc. and its affiliates.
2 //
3 // This source code is licensed under the MIT license found in the
4 // LICENSE file in the "hack" directory of this source tree.
6 extern crate lazy_static;
8 use escaper::*;
9 use lazy_static::lazy_static;
10 use naming_special_names_rust::{classes as ns_classes, members};
11 use regex::Regex;
12 use std::borrow::Cow;
13 use std::cell::Cell;
15 lazy_static! {
16     static ref HH_NS_RE: Regex = Regex::new(r"^\\?HH\\").unwrap();
17     static ref NS_RE: Regex = Regex::new(r".*\\").unwrap();
18     static ref TYPE_RE: Regex = Regex::new(r"<.*>").unwrap();
21 #[derive(Clone)]
22 pub struct GetName {
23     string: Vec<u8>,
25     unescape: fn(String) -> String,
28 impl GetName {
29     pub fn new(string: Vec<u8>, unescape: fn(String) -> String) -> GetName {
30         GetName { string, unescape }
31     }
33     pub fn get(&self) -> &Vec<u8> {
34         &self.string
35     }
36     pub fn to_string(&self) -> String {
37         String::from_utf8_lossy(&self.string).to_string()
38     }
39     pub fn to_unescaped_string(&self) -> String {
40         let unescape = self.unescape;
41         unescape(self.to_string())
42     }
45 impl std::fmt::Debug for GetName {
46     fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
47         write!(f, "GetName {{ string: {}, unescape:? }}", self.to_string())
48     }
51 thread_local!(static MANGLE_XHP_MODE: Cell<bool> = Cell::new(true));
53 pub fn without_xhp_mangling<T>(f: impl FnOnce() -> T) -> T {
54     MANGLE_XHP_MODE.with(|cur| {
55         let old = cur.replace(false);
56         let ret = f();
57         cur.set(old); // use old instead of true to support nested calls in the same thread
58         ret
59     })
62 pub fn is_xhp(name: &str) -> bool {
63     name.chars().next().map_or(false, |c| c == ':')
66 pub fn clean(s: &str) -> &str {
67     if is_xhp(s) { strip_colon(s) } else { s }
70 fn strip_colon(s: &str) -> &str {
71     s.trim_start_matches(":")
74 fn ignore_id(name: &str) -> bool {
75     name.starts_with("Closure$")
78 pub fn mangle_xhp_id(mut name: String) -> String {
79     if !ignore_id(&name) && MANGLE_XHP_MODE.with(|x| x.get()) {
80         if is_xhp(&name) {
81             name.replace_range(..1, "xhp_")
82         }
83         name.replace(":", "__").replace("-", "_")
84     } else {
85         name
86     }
89 fn unmangle_xhp_id(name: &str) -> String {
90     if name.starts_with("xhp_") {
91         format!(
92             ":{}",
93             lstrip(name, "xhp_").replace("__", ":").replace("_", "-")
94         )
95     } else {
96         name.replace("__", ":").replace("_", "-")
97     }
100 pub fn mangle(mut name: String) -> String {
101     if !ignore_id(&name) {
102         if let Some(pos) = name.rfind('\\') {
103             name.replace_range(pos + 1.., &mangle_xhp_id(name[pos + 1..].to_string()))
104         } else {
105             name = mangle_xhp_id(name);
106         }
107     }
108     name
111 pub fn unmangle(name: String) -> String {
112     if ignore_id(&name) {
113         return name;
114     }
115     let ids = name.split("\\").collect::<Vec<_>>();
116     match ids.split_last() {
117         None => String::new(),
118         Some((last, rest)) => {
119             if rest.is_empty() {
120                 unmangle_xhp_id(last)
121             } else {
122                 format!("{}\\{}", rest.join("\\"), unmangle_xhp_id(last))
123             }
124         }
125     }
128 pub fn quote_string(s: &str) -> String {
129     format!("\"{}\"", escape(s))
132 pub fn quote_string_with_escape(s: &str) -> String {
133     format!("\\\"{}\\\"", escape(s))
136 pub fn single_quote_string_with_escape(s: &str) -> String {
137     format!("'{}'", escape(s))
140 pub fn triple_quote_string(s: &str) -> String {
141     format!("\"\"\"{}\"\"\"", escape(s))
144 pub fn prefix_namespace(n: &str, s: &str) -> String {
145     format!("{}\\{}", n, s)
148 pub fn strip_global_ns(s: &str) -> &str {
149     if s.starts_with("\\") { &s[1..] } else { s }
152 // Strip zero or more chars followed by a backslash
153 pub fn strip_ns(s: &str) -> &str {
154     s.rfind('\\').map_or(s, |i| &s[i + 1..])
157 // Remove \HH\ or HH\ preceding a string
158 pub fn strip_hh_ns(s: &str) -> Cow<str> {
159     HH_NS_RE.replace(&s, "")
162 pub fn has_ns(s: &str) -> bool {
163     NS_RE.is_match(s)
166 pub fn strip_type_list(s: &str) -> Cow<str> {
167     TYPE_RE.replace_all(&s, "")
170 pub fn cmp(s1: &str, s2: &str, case_sensitive: bool, ignore_ns: bool) -> bool {
171     fn canon(s: &str, ignore_ns: bool) -> &str {
172         if ignore_ns { strip_ns(s) } else { s }
173     }
175     let s1 = canon(s1, ignore_ns);
176     let s2 = canon(s2, ignore_ns);
178     if case_sensitive {
179         s1 == s2
180     } else {
181         s1.eq_ignore_ascii_case(&s2)
182     }
185 pub fn is_self(s: impl AsRef<str>) -> bool {
186     s.as_ref().eq_ignore_ascii_case(ns_classes::SELF)
189 pub fn is_parent(s: impl AsRef<str>) -> bool {
190     s.as_ref().eq_ignore_ascii_case(ns_classes::PARENT)
193 pub fn is_static(s: impl AsRef<str>) -> bool {
194     s.as_ref().eq_ignore_ascii_case(ns_classes::STATIC)
197 pub fn is_class(s: impl AsRef<str>) -> bool {
198     s.as_ref().eq_ignore_ascii_case(members::M_CLASS)
201 pub fn mangle_meth_caller(mangled_cls_name: &str, f_name: &str) -> String {
202     format!("\\MethCaller${}${}", mangled_cls_name, f_name)
205 pub fn lstrip<'a>(s: &'a str, p: &str) -> &'a str {
206     if s.len() < p.len() {
207         s
208     } else {
209         let sb = s.as_bytes();
210         let pb = p.as_bytes();
211         for i in 0..pb.len() {
212             if sb[i] != pb[i] {
213                 return s;
214             }
215         }
216         // s and p are valid then unwrap should never panic.
217         std::str::from_utf8(&sb[pb.len()..]).unwrap()
218     }
221 pub mod types {
222     pub fn fix_casing(s: &str) -> &str {
223         match s.to_lowercase().as_str() {
224             "vector" => "Vector",
225             "immvector" => "ImmVector",
226             "set" => "Set",
227             "immset" => "ImmSet",
228             "map" => "Map",
229             "immmap" => "ImmMap",
230             "pair" => "Pair",
231             _ => s,
232         }
233     }
236 /* Integers are represented as strings */
237 pub mod integer {
238     pub fn to_decimal(s: &str) -> Option<String> {
239         /* Don't accidentally convert 0 to 0o */
240         let r = if s.len() > 1
241             && s.as_bytes()[0] == b'0'
242             && s.as_bytes()[1] >= b'0'
243             && s.as_bytes()[1] <= b'9'
244         {
245             ocaml_helper::int_of_string_wrap(format!("0o{}", &s[1..]).as_bytes())
246         } else {
247             ocaml_helper::int_of_string_wrap(s.as_bytes())
248         };
249         r.map(|n| n.to_string())
250     }
253 pub mod float {
254     fn sprintf(f: f64) -> Option<String> {
255         const BUF_SIZE: usize = 256;
257         let format = "%.17g\0";
258         let mut buffer = [0u8; BUF_SIZE];
259         let n = unsafe {
260             libc::snprintf(
261                 buffer.as_mut_ptr() as *mut libc::c_char,
262                 BUF_SIZE,
263                 format.as_ptr() as *const libc::c_char,
264                 f,
265             ) as usize
266         };
267         if n >= BUF_SIZE {
268             None
269         } else {
270             String::from_utf8(buffer[..n].to_vec()).ok()
271         }
272     }
274     pub fn to_string(f: impl Into<f64>) -> String {
275         let f = f.into();
276         // or_else should not happen, but just in case it does fall back
277         // to Rust native formatting
278         let res = sprintf(f).unwrap_or_else(|| f.to_string());
279         match res.as_ref() {
280             "-nan" => "NAN".to_string(),
281             "nan" => "NAN".to_string(),
282             "-inf" => "-INF".to_string(),
283             "inf" => "INF".to_string(),
284             _ => res,
285         }
286     }
289 pub mod locals {
290     pub fn strip_dollar(s: &str) -> &str {
291         if s.len() > 0 && s.as_bytes()[0] == b'$' {
292             &s[1..]
293         } else {
294             s
295         }
296     }
299 pub mod classes {
300     pub fn mangle_class(prefix: &str, scope: &str, idx: u32) -> String {
301         if idx == 1 {
302             format!("{}${}", prefix.to_string(), scope.to_string())
303         } else {
304             format!(
305                 "{}${}#{}",
306                 prefix.to_string(),
307                 scope.to_string(),
308                 idx.to_string()
309             )
310         }
311     }
314 pub mod closures {
315     pub fn mangle_closure(scope: &str, idx: u32) -> String {
316         super::classes::mangle_class("Closure", scope, idx)
317     }
319     /* Closure classes have names of the form
320      *   Closure$ scope ix ; num
321      * where
322      *   scope  ::=
323      *     <function-name>
324      *   | <class-name> :: <method-name>
325      *   |
326      *   ix ::=
327      *     # <digits>
328      */
329     pub fn unmangle_closure(mangled_name: &str) -> Option<&str> {
330         if is_closure_name(mangled_name) {
331             let prefix_length = "Closure$".chars().count();
332             match mangled_name.find('#') {
333                 Some(pos) => Some(&mangled_name[prefix_length..pos]),
334                 None => Some(&mangled_name[prefix_length..]),
335             }
336         } else {
337             None
338         }
339     }
341     pub fn is_closure_name(s: &str) -> bool {
342         s.starts_with("Closure$")
343     }
346 pub mod reified {
347     pub static PROP_NAME: &'static str = "86reified_prop";
348     pub static INIT_METH_NAME: &'static str = "86reifiedinit";
349     pub static INIT_METH_PARAM_NAME: &'static str = "$__typestructures";
350     pub static GENERICS_LOCAL_NAME: &'static str = "$0ReifiedGenerics";
351     pub static CAPTURED_PREFIX: &'static str = "$__captured$reifiedgeneric$";
353     pub fn reified_generic_captured_name(is_fun: bool, i: usize) -> String {
354         let type_ = if is_fun { "function" } else { "class" };
355         // to_string() due to T52404885
356         format!("$__captured$reifiedgeneric${}${}", type_, i.to_string())
357     }
359     pub fn mangle_reified_param(no_dollar: bool, s: &str) -> String {
360         format!("{}__reified${}", if no_dollar { "" } else { "$" }, s)
361     }
363     pub fn captured_name(is_fun: bool, i: usize) -> String {
364         format!(
365             "{}{}${}",
366             CAPTURED_PREFIX,
367             if is_fun { "function" } else { "class" },
368             i
369         )
370     }
372     pub fn is_captured_generic(id: &str) -> Option<(bool, u32)> {
373         if id.starts_with(CAPTURED_PREFIX) {
374             if let [name, i] = id
375                 .trim_start_matches(CAPTURED_PREFIX)
376                 .splitn(2, "$")
377                 .collect::<Vec<_>>()
378                 .as_slice()
379             {
380                 let is_captured = match *name {
381                     "function" => true,
382                     "class" => false,
383                     _ => return None,
384                 };
385                 let captured_id = i.parse();
386                 if captured_id.is_ok() {
387                     return Some((is_captured, captured_id.unwrap()));
388                 };
389             }
390         };
391         None
392     }
395 #[cfg(test)]
396 mod string_utils_tests {
397     use pretty_assertions::assert_eq;
399     #[test]
400     fn quote_string_test() {
401         let some_string = "test";
402         assert_eq!(super::quote_string(&some_string), "\"test\"");
403     }
405     #[test]
406     fn quote_string_with_escape_test() {
407         let some_string = "test";
408         assert_eq!(
409             super::quote_string_with_escape(&some_string),
410             "\\\"test\\\""
411         );
412     }
414     #[test]
415     fn single_quote_string_with_escape_test() {
416         let some_string = "test";
417         assert_eq!(
418             super::single_quote_string_with_escape(&some_string),
419             "'test'"
420         );
421     }
423     #[test]
424     fn triple_quote_string_test() {
425         let some_string = "test";
426         assert_eq!(super::triple_quote_string(&some_string), "\"\"\"test\"\"\"");
427     }
429     #[test]
430     fn prefix_namespace_test() {
431         let namespace = "ns";
432         let some_string = "test";
433         assert_eq!(
434             super::prefix_namespace(&namespace, &some_string),
435             "ns\\test"
436         );
437     }
439     #[test]
440     fn strip_global_ns_test() {
441         let some_string = "\\test";
442         let another_string = "\\\\";
443         assert_eq!(super::strip_global_ns(&some_string), "test");
444         assert_eq!(super::strip_global_ns(&another_string), "\\");
445     }
447     #[test]
448     fn strip_ns_test() {
449         let with_ns = "ns1\\test";
450         let without_ns = "test";
451         assert_eq!(super::strip_ns(&with_ns), "test");
452         assert_eq!(super::strip_ns(&without_ns), without_ns);
453     }
455     #[test]
456     fn strip_hh_ns() {
457         let with_ns = "HH\\test";
458         let without_ns = "test";
459         assert_eq!(super::strip_ns(&with_ns), "test");
460         assert_eq!(super::strip_ns(&without_ns), without_ns);
461     }
463     #[test]
464     fn strip_hh_ns_2() {
465         let with_ns = "\\HH\\test";
466         let without_ns = "test";
467         assert_eq!(super::strip_ns(&with_ns), "test");
468         assert_eq!(super::strip_ns(&without_ns), without_ns);
469     }
471     #[test]
472     fn has_ns_test() {
473         let with_ns = "\\test";
474         let without_ns = "test";
475         assert_eq!(super::has_ns(&with_ns), true);
476         assert_eq!(super::has_ns(&without_ns), false);
477     }
479     #[test]
480     fn strip_type_list_test() {
481         let s = "MutableMap<Tk, Tv>";
482         assert_eq!(super::strip_type_list(&s).into_owned(), "MutableMap");
483     }
485     #[test]
486     fn cmp_test() {
487         let s1 = "ns1\\s1";
488         let s1_uppercase = "NS1\\S1";
490         let ns2_s1 = "ns2\\s1";
491         let ns2_s1_uppercase = "NS2\\S1";
493         let ns2_s2 = "ns2\\s2";
495         assert_eq!(true, super::cmp(&s1, &s1_uppercase, false, false));
496         assert_eq!(false, super::cmp(&s1, &s1_uppercase, true, false));
497         assert_eq!(true, super::cmp(&s1, &s1_uppercase, false, true));
498         assert_eq!(false, super::cmp(&s1, &s1_uppercase, true, true));
500         assert_eq!(false, super::cmp(&s1, &ns2_s1, false, false));
501         assert_eq!(false, super::cmp(&s1, &ns2_s1, true, false));
502         assert_eq!(true, super::cmp(&s1, &ns2_s1, false, true));
503         assert_eq!(true, super::cmp(&s1, &ns2_s1, true, true));
505         assert_eq!(false, super::cmp(&s1, &ns2_s1_uppercase, false, false));
506         assert_eq!(false, super::cmp(&s1, &ns2_s1_uppercase, true, false));
507         assert_eq!(true, super::cmp(&s1, &ns2_s1_uppercase, false, true));
508         assert_eq!(false, super::cmp(&s1, &ns2_s1_uppercase, true, true));
510         assert_eq!(false, super::cmp(&s1, &ns2_s2, false, false));
511         assert_eq!(false, super::cmp(&s1, &ns2_s2, true, false));
512         assert_eq!(false, super::cmp(&s1, &ns2_s2, false, true));
513         assert_eq!(false, super::cmp(&s1, &ns2_s2, true, true));
514     }
516     #[test]
517     fn is_self_test() {
518         let s1 = "self";
519         let s2 = "not_self";
521         assert_eq!(super::is_self(&s1), true);
522         assert_eq!(super::is_self(&s2), false);
523     }
525     #[test]
526     fn is_parent_test() {
527         let s1 = "parent";
528         let s2 = "not_parent";
530         assert_eq!(super::is_parent(&s1), true);
531         assert_eq!(super::is_parent(&s2), false);
532     }
534     #[test]
535     fn is_static_test() {
536         let s1 = "static";
537         let s2 = "not_static";
539         assert_eq!(super::is_static(&s1), true);
540         assert_eq!(super::is_static(&s2), false);
541     }
543     #[test]
544     fn is_class_test() {
545         let s1 = "class";
546         let s2 = "not_a_class";
548         assert_eq!(super::is_class(&s1), true);
549         assert_eq!(super::is_class(&s2), false);
550     }
552     #[test]
553     fn mangle_meth_caller_test() {
554         let cls = "SomeClass";
555         let f = "some_function";
557         assert_eq!(
558             super::mangle_meth_caller(cls, f),
559             "\\MethCaller$SomeClass$some_function"
560         );
561     }
563     #[test]
564     fn mangle_test_1() {
565         assert_eq!(
566             super::mangle(":foo:bar-and-baz".into()),
567             "xhp_foo__bar_and_baz"
568         );
569     }
571     #[test]
572     fn mangle_test_2() {
573         assert_eq!(super::mangle("\\:base".into()), "\\xhp_base");
574     }
576     #[test]
577     fn mangle_test_3() {
578         assert_eq!(
579             super::mangle("\\NS1\\NS2\\:base".into()),
580             "\\NS1\\NS2\\xhp_base"
581         );
582     }
584     mod types {
585         mod fix_casing {
586             macro_rules! test_case {
587                 ($name: ident, $input: expr, $expected: expr) => {
588                     #[test]
589                     fn $name() {
590                         assert_eq!(crate::types::fix_casing($input), $expected);
591                     }
592                 };
593             }
595             test_case!(lowercase_vector, "vector", "Vector");
596             test_case!(mixedcase_vector, "vEcTor", "Vector");
597             test_case!(uppercase_vector, "VECTOR", "Vector");
599             test_case!(lowercase_immvector, "immvector", "ImmVector");
600             test_case!(mixedcase_immvector, "immvEcTor", "ImmVector");
601             test_case!(uppercase_immvector, "IMMVECTOR", "ImmVector");
603             test_case!(lowercase_set, "set", "Set");
604             test_case!(mixedcase_set, "SeT", "Set");
605             test_case!(uppercase_set, "SET", "Set");
607             test_case!(lowercase_immset, "immset", "ImmSet");
608             test_case!(mixedcase_immset, "ImMSeT", "ImmSet");
609             test_case!(uppercase_immset, "IMMSET", "ImmSet");
611             test_case!(lowercase_map, "map", "Map");
612             test_case!(mixedcase_map, "MaP", "Map");
613             test_case!(uppercase_map, "MAP", "Map");
615             test_case!(lowercase_immmap, "immmap", "ImmMap");
616             test_case!(mixedcase_immmap, "immMaP", "ImmMap");
617             test_case!(uppercase_immmap, "IMMMAP", "ImmMap");
619             test_case!(lowercase_pair, "pair", "Pair");
620             test_case!(mixedcase_pair, "pAiR", "Pair");
621             test_case!(uppercase_pair, "PAIR", "Pair");
623             test_case!(
624                 non_hack_collection_returns_original_string,
625                 "SomeStRinG",
626                 "SomeStRinG"
627             );
628             test_case!(
629                 hack_collection_with_leading_whitespace_returns_original_string,
630                 " pair",
631                 " pair"
632             );
633             test_case!(
634                 hack_collection_with_trailing_whitespace_returns_original_string,
635                 "pair ",
636                 "pair "
637             );
638         }
639     }
641     mod float {
642         use crate::float::*;
643         #[test]
644         fn test_no_float_part() {
645             assert_eq!(to_string(1.0), "1")
646         }
648         #[test]
649         fn test_precision() {
650             assert_eq!(to_string(1.1), "1.1000000000000001")
651         }
653         #[test]
654         fn test_no_trailing_zeroes() {
655             assert_eq!(to_string(1.2), "1.2")
656         }
658         #[test]
659         fn test_scientific() {
660             assert_eq!(to_string(1e+100), "1e+100")
661         }
663         #[test]
664         fn test_scientific_precision() {
665             assert_eq!(to_string(-2.1474836480001e9), "-2147483648.0001001")
666         }
668         #[test]
669         fn test_negative_nan() {
670             assert_eq!(to_string(-std::f32::NAN), "NAN")
671         }
672     }
674     mod integer {
675         mod to_decimal {
676             use crate::integer::*;
678             #[test]
679             fn decimal_zero() {
680                 assert_eq!(to_decimal("0"), Some("0".to_string()));
681             }
683             #[test]
684             fn octal_zero() {
685                 assert_eq!(to_decimal("00"), Some("0".to_string()));
686             }
688             #[test]
689             fn binary_zero_lowercase() {
690                 assert_eq!(to_decimal("0b0"), Some("0".to_string()));
691             }
693             #[test]
694             fn binary_zero_uppercase() {
695                 assert_eq!(to_decimal("0B0"), Some("0".to_string()));
696             }
698             #[test]
699             fn hex_zero_lowercase() {
700                 assert_eq!(to_decimal("0x0"), Some("0".to_string()));
701             }
703             #[test]
704             fn hex_zero_uppercase() {
705                 assert_eq!(to_decimal("0X0"), Some("0".to_string()));
706             }
708             #[test]
709             fn decimal_random_value() {
710                 assert_eq!(to_decimal("1245"), Some("1245".to_string()));
711             }
713             #[test]
714             fn octal_random_value() {
715                 assert_eq!(to_decimal("02335"), Some("1245".to_string()));
716             }
718             #[test]
719             fn binary_random_value_lowercase() {
720                 assert_eq!(to_decimal("0b10011011101"), Some("1245".to_string()));
721             }
723             #[test]
724             fn binary_random_value_uppercase() {
725                 assert_eq!(to_decimal("0B10011011101"), Some("1245".to_string()));
726             }
728             #[test]
729             fn hex_random_value_lowercase() {
730                 assert_eq!(to_decimal("0x4DD"), Some("1245".to_string()));
731             }
733             #[test]
734             fn hex_random_value_uppercase() {
735                 assert_eq!(to_decimal("0X4DD"), Some("1245".to_string()));
736             }
738             #[test]
739             fn decimal_max_value() {
740                 assert_eq!(
741                     to_decimal("9223372036854775807"),
742                     Some("9223372036854775807".to_string())
743                 );
744             }
746             #[test]
747             fn octal_max_value() {
748                 assert_eq!(
749                     to_decimal("0777777777777777777777"),
750                     Some("9223372036854775807".to_string())
751                 );
752             }
754             #[test]
755             fn binary_max_value_lowercase() {
756                 assert_eq!(
757                     to_decimal("0b111111111111111111111111111111111111111111111111111111111111111"),
758                     Some("9223372036854775807".to_string())
759                 );
760             }
762             #[test]
763             fn binary_max_value_uppercase() {
764                 assert_eq!(
765                     to_decimal("0B111111111111111111111111111111111111111111111111111111111111111"),
766                     Some("9223372036854775807".to_string())
767                 );
768             }
770             #[test]
771             fn hex_max_value_lowercase() {
772                 assert_eq!(
773                     to_decimal("0x7FFFFFFFFFFFFFFF"),
774                     Some("9223372036854775807".to_string())
775                 );
776             }
778             #[test]
779             fn hex_max_value_uppercase() {
780                 assert_eq!(
781                     to_decimal("0X7FFFFFFFFFFFFFFF"),
782                     Some("9223372036854775807".to_string())
783                 );
784             }
786             #[test]
787             fn unparsable_string() {
788                 assert!(to_decimal("bad_string").is_none());
789             }
790         }
791     }
793     mod locals {
794         use crate::locals::*;
796         #[test]
797         fn strip_single_leading_dollar() {
798             assert_eq!(strip_dollar("$foo"), "foo");
799         }
801         #[test]
802         fn return_string_if_no_leading_dollar() {
803             assert_eq!(strip_dollar("foo"), "foo");
804         }
806         #[test]
807         fn empty_string() {
808             assert_eq!(strip_dollar(""), "");
809         }
811         #[test]
812         fn string_of_single_dollar() {
813             assert_eq!(strip_dollar("$"), "");
814         }
815     }
817     mod classes {
818         mod mangle_class {
819             use crate::classes::mangle_class;
821             #[test]
822             fn idx_of_one() {
823                 assert_eq!(mangle_class("foo", "bar", 1), "foo$bar")
824             }
826             #[test]
827             fn idx_of_two() {
828                 assert_eq!(mangle_class("foo", "bar", 2), "foo$bar#2")
829             }
830         }
831     }
833     mod closures {
834         mod mangle_closure {
835             use crate::closures::mangle_closure;
837             #[test]
838             fn idx_of_one() {
839                 assert_eq!(mangle_closure("foo", 1), "Closure$foo")
840             }
842             #[test]
843             fn idx_of_two() {
844                 assert_eq!(mangle_closure("foo", 2), "Closure$foo#2")
845             }
846         }
848         mod unmangle_closure {
849             use crate::closures::unmangle_closure;
851             #[test]
852             fn idx_of_one() {
853                 assert_eq!(unmangle_closure("Closure$foo"), Some("foo"))
854             }
856             #[test]
857             fn idx_of_two() {
858                 assert_eq!(unmangle_closure("Closure$foo#2"), Some("foo"))
859             }
861             #[test]
862             fn non_closure() {
863                 assert_eq!(unmangle_closure("SomePrefix$foo"), None);
864                 assert_eq!(unmangle_closure("SomePrefix$foo#2"), None)
865             }
866         }
868         mod is_closure_name {
869             use crate::closures::is_closure_name;
871             #[test]
872             fn closure_1() {
873                 assert_eq!(is_closure_name("Closure$foo"), true)
874             }
876             #[test]
877             fn closure_2() {
878                 assert_eq!(is_closure_name("Closure$foo#2"), true)
879             }
881             #[test]
882             fn non_closure() {
883                 assert_eq!(is_closure_name("SomePrefix$foo"), false);
884                 assert_eq!(is_closure_name("SomePrefix$foo#2"), false)
885             }
886         }
887     }
889     mod reified {
890         use crate::reified::*;
892         #[test]
893         fn test_mangle_reified_param() {
894             assert_eq!(mangle_reified_param(false, "x"), "$__reified$x");
895             assert_eq!(mangle_reified_param(true, "x"), "__reified$x")
896         }
898         #[test]
899         fn test_is_captured_generic() {
900             assert_eq!(
901                 is_captured_generic("$__captured$reifiedgeneric$function$1"),
902                 Some((true, 1))
903             );
904             assert_eq!(
905                 is_captured_generic("$__captured$reifiedgeneric$class$1"),
906                 Some((false, 1))
907             );
908             assert_eq!(is_captured_generic("function$1"), None);
909             assert_eq!(
910                 is_captured_generic("$__captured$reifiedgeneric$function1"),
911                 None
912             );
913         }
915         #[test]
916         fn test_captured_name() {
917             assert_eq!(
918                 captured_name(true, 1),
919                 "$__captured$reifiedgeneric$function$1"
920             );
921             assert_eq!(
922                 captured_name(false, 1),
923                 "$__captured$reifiedgeneric$class$1"
924             );
925         }
926     }
928     #[test]
929     fn test_lstrip() {
930         use super::lstrip;
931         assert_eq!(lstrip("a", "a"), "");
932         assert_eq!(lstrip("a", "ab"), "a");
933         assert_eq!(lstrip("", "ab"), "");
934         assert_eq!(lstrip("", ""), "");
935         assert_eq!(lstrip("a", ""), "a");
936         assert_eq!(lstrip("aa", "a"), "a");
937         assert_eq!(lstrip("aa", "a"), "a");
938     }