3
3
use std:: { borrow:: Cow , sync:: OnceLock } ;
4
4
5
5
use super :: Value ;
6
- use regex:: { Captures , Regex , Replacer } ;
6
+ use regex:: { Captures , Match , Regex , Replacer } ;
7
7
8
- /// Replaces a value in a string.
9
- struct ValueReplacer < ' a , ' b , GetValueFunc >
8
+ /// A regular expression replacer implementation for replacing formatted
9
+ /// values in a display macro string.
10
+ struct DisReplacer < ' a , ' b , GetValueFunc , GetLocalizedFunc >
10
11
where
11
12
GetValueFunc : Fn ( & str ) -> Option < & ' a Value > ,
13
+ GetLocalizedFunc : Fn ( & str ) -> Option < Cow < ' a , str > > ,
12
14
{
13
15
get_value : & ' b GetValueFunc ,
16
+
17
+ get_localized : & ' b GetLocalizedFunc ,
14
18
}
15
19
16
- impl < ' a , ' b , GetValueFunc > Replacer for ValueReplacer < ' a , ' b , GetValueFunc >
20
+ impl < ' a , ' b , GetValueFunc , GetLocalizedFunc > Replacer
21
+ for DisReplacer < ' a , ' b , GetValueFunc , GetLocalizedFunc >
17
22
where
18
23
GetValueFunc : Fn ( & str ) -> Option < & ' a Value > ,
24
+ GetLocalizedFunc : Fn ( & str ) -> Option < Cow < ' a , str > > ,
19
25
{
20
26
fn replace_append ( & mut self , caps : & Captures < ' _ > , dst : & mut String ) {
21
- if let Some ( cap_match ) = caps . get ( 1 ) {
27
+ let mut handle_value_capture = | cap_match : Match < ' _ > | {
22
28
if let Some ( value) = ( self . get_value ) ( cap_match. as_str ( ) ) {
23
29
if let Value :: Ref ( val) = value {
24
30
dst. push_str ( val. dis . as_ref ( ) . unwrap_or ( & val. value ) ) ;
@@ -30,26 +36,16 @@ where
30
36
} else {
31
37
dst. push_str ( caps. get ( 0 ) . unwrap ( ) . as_str ( ) ) ;
32
38
}
33
- } else {
34
- dst. push_str ( caps. get ( 0 ) . unwrap ( ) . as_str ( ) ) ;
35
- }
36
- }
37
- }
38
-
39
- /// Replaces a localized value in a string.
40
- struct LocalizedReplacer < ' a , ' b , GetLocalizedFunc >
41
- where
42
- GetLocalizedFunc : Fn ( & str ) -> Option < Cow < ' a , str > > ,
43
- {
44
- get_localized : & ' b GetLocalizedFunc ,
45
- }
46
-
47
- impl < ' a , ' b , GetLocalizedFunc > Replacer for LocalizedReplacer < ' a , ' b , GetLocalizedFunc >
48
- where
49
- GetLocalizedFunc : Fn ( & str ) -> Option < Cow < ' a , str > > ,
50
- {
51
- fn replace_append ( & mut self , caps : & Captures < ' _ > , dst : & mut String ) {
52
- if let Some ( cap_match) = caps. get ( 1 ) {
39
+ } ;
40
+
41
+ // Replace $tag
42
+ if let Some ( cap_match) = caps. get ( 2 ) {
43
+ handle_value_capture ( cap_match) ;
44
+ // Replace ${tag}
45
+ } else if let Some ( cap_match) = caps. get ( 4 ) {
46
+ handle_value_capture ( cap_match) ;
47
+ // Replace $<pod::key>
48
+ } else if let Some ( cap_match) = caps. get ( 6 ) {
53
49
if let Some ( value) = ( self . get_localized ) ( cap_match. as_str ( ) ) {
54
50
dst. push_str ( & value) ;
55
51
} else {
@@ -63,70 +59,39 @@ where
63
59
64
60
/// Process a macro pattern with the given scope of name/value pairs. Also includes localization processing.
65
61
///
66
- /// * The pattern is a unicode string with embedded expressions:
67
- /// * ' $tag' : resolve tag name from scope, variable name ends with first non-tag character.
68
- /// * ' ${tag}' : resolve tag name from scope.
69
- /// * ' $<pod::key> localization key.
62
+ /// The pattern is a unicode string with embedded expressions:
63
+ /// * ` $tag` : resolve tag name from scope, variable name ends with first non-tag character.
64
+ /// * ` ${tag}` : resolve tag name from scope.
65
+ /// * ` $<pod::key>` localization key.
70
66
///
71
- /// Any variables which cannot be resolved are resolved in the scope are returned as-is (i.e. $name)
67
+ /// Any variables which cannot be resolved are resolved in the scope are returned as-is (i.e. ` $name` )
72
68
/// in the result string.
73
69
///
74
- /// If a tag resolves to a Ref, then we use the Ref.dis for the string.
70
+ /// If a tag resolves to a ` Ref` , then we use the ` Ref.dis` for the string.
75
71
pub fn dis_macro < ' a , GetValueFunc , GetLocalizedFunc > (
76
72
pattern : & ' a str ,
77
73
get_value : GetValueFunc ,
78
74
get_localized : GetLocalizedFunc ,
79
- ) -> String
75
+ ) -> Cow < ' a , str >
80
76
where
81
77
GetValueFunc : Fn ( & str ) -> Option < & ' a Value > ,
82
78
GetLocalizedFunc : Fn ( & str ) -> Option < Cow < ' a , str > > ,
83
79
{
84
- // Replace $tag
85
- let result = {
86
- static VARIABLE_REG_EX : OnceLock < Regex > = OnceLock :: new ( ) ;
87
-
88
- let variable_regex =
89
- VARIABLE_REG_EX . get_or_init ( || Regex :: new ( r"\$([a-z][a-zA-Z0-9_]+)" ) . unwrap ( ) ) ;
90
-
91
- variable_regex. replace_all (
92
- pattern,
93
- ValueReplacer {
94
- get_value : & get_value,
95
- } ,
96
- )
97
- } ;
98
-
99
- // Replace ${tag}
100
- let result = {
101
- static SCOPE_REG_EX : OnceLock < Regex > = OnceLock :: new ( ) ;
102
-
103
- let scope_regex =
104
- SCOPE_REG_EX . get_or_init ( || Regex :: new ( r"\$\{([a-z][a-zA-Z0-9_]+)\}" ) . unwrap ( ) ) ;
105
-
106
- scope_regex. replace_all (
107
- & result,
108
- ValueReplacer {
109
- get_value : & get_value,
110
- } ,
111
- )
112
- } ;
113
-
114
- // Replace $<pod::key>
115
- let result = {
116
- static LOCALIZED_REG_EX : OnceLock < Regex > = OnceLock :: new ( ) ;
117
-
118
- let localized_regex = LOCALIZED_REG_EX . get_or_init ( || Regex :: new ( r"\$<([^>]+)>" ) . unwrap ( ) ) ;
119
-
120
- localized_regex. replace_all (
121
- & result,
122
- LocalizedReplacer {
123
- get_localized : & get_localized,
124
- } ,
125
- )
126
- } ;
127
-
128
- // TODO: return Cow?
129
- result. to_string ( )
80
+ // Cache the regular expression in memory so it doesn't need to compile on each invocation.
81
+ static REG_EX : OnceLock < Regex > = OnceLock :: new ( ) ;
82
+
83
+ let regex = REG_EX . get_or_init ( || {
84
+ // Replace $tags, ${tag} or $<pod::key>
85
+ Regex :: new ( r"(\$([a-z][a-zA-Z0-9_]+))|(\$\{([a-z][a-zA-Z0-9_]+)\})|(\$<([^>]+)>)" ) . unwrap ( )
86
+ } ) ;
87
+
88
+ regex. replace_all (
89
+ pattern,
90
+ DisReplacer {
91
+ get_value : & get_value,
92
+ get_localized : & get_localized,
93
+ } ,
94
+ )
130
95
}
131
96
132
97
#[ cfg( test) ]
0 commit comments