@@ -6,9 +6,9 @@ use fluent_fallback::{
6
6
} ;
7
7
use futures:: stream:: Stream ;
8
8
use rustc_hash:: FxHashSet ;
9
- use std:: fs;
10
9
use std:: io;
11
- use std:: iter;
10
+ use std:: { fs, iter} ;
11
+ use thiserror:: Error ;
12
12
use unic_langid:: LanguageIdentifier ;
13
13
14
14
fn read_file ( path : & str ) -> Result < String , io:: Error > {
@@ -48,21 +48,24 @@ impl ResourceManager {
48
48
49
49
/// Returns a [`FluentResource`], by either reading the file and loading it into
50
50
/// memory, or retrieving it from an in-memory cache.
51
- fn get_resource ( & self , resource_id : & str , locale : & str ) -> & FluentResource {
51
+ fn get_resource (
52
+ & self ,
53
+ res_id : & str ,
54
+ locale : & str ,
55
+ ) -> Result < & FluentResource , ResourceManagerError > {
52
56
let path = self
53
57
. path_scheme
54
58
. replace ( "{locale}" , locale)
55
- . replace ( "{res_id}" , resource_id ) ;
56
- if let Some ( res ) = self . resources . get ( & path) {
57
- res
59
+ . replace ( "{res_id}" , res_id ) ;
60
+ Ok ( if let Some ( resource ) = self . resources . get ( & path) {
61
+ resource
58
62
} else {
59
- let string = read_file ( & path) . unwrap ( ) ;
60
- let res = match FluentResource :: try_new ( string) {
61
- Ok ( res) => res,
62
- Err ( ( res, _err) ) => res,
63
+ let resource = match FluentResource :: try_new ( read_file ( & path) ?) {
64
+ Ok ( resource) => resource,
65
+ Err ( ( resource, _err) ) => resource,
63
66
} ;
64
- self . resources . insert ( path. to_string ( ) , Box :: new ( res ) )
65
- }
67
+ self . resources . insert ( path. to_string ( ) , Box :: new ( resource ) )
68
+ } )
66
69
}
67
70
68
71
/// Gets a [`FluentBundle`] from a list of resources. The bundle will only contain the
@@ -74,14 +77,28 @@ impl ResourceManager {
74
77
& self ,
75
78
locales : Vec < LanguageIdentifier > ,
76
79
resource_ids : Vec < String > ,
77
- ) -> FluentBundle < & FluentResource > {
80
+ ) -> Result < FluentBundle < & FluentResource > , Vec < ResourceManagerError > > {
81
+ let mut errors: Vec < ResourceManagerError > = vec ! [ ] ;
78
82
let mut bundle = FluentBundle :: new ( locales. clone ( ) ) ;
79
- for res_id in & resource_ids {
80
- println ! ( "res_id {:?}" , res_id) ;
81
- let res = self . get_resource ( res_id, & locales[ 0 ] . to_string ( ) ) ;
82
- bundle. add_resource ( res) . unwrap ( ) ;
83
+ let locale = & locales[ 0 ] ;
84
+
85
+ for resource_id in & resource_ids {
86
+ match self . get_resource ( resource_id, & locale. to_string ( ) ) {
87
+ Ok ( resource) => {
88
+ if let Err ( errs) = bundle. add_resource ( resource) {
89
+ errs. into_iter ( )
90
+ . for_each ( |error| errors. push ( ResourceManagerError :: Fluent ( error) ) )
91
+ }
92
+ }
93
+ Err ( error) => errors. push ( error) ,
94
+ } ;
95
+ }
96
+
97
+ if !errors. is_empty ( ) {
98
+ Err ( errors)
99
+ } else {
100
+ Ok ( bundle)
83
101
}
84
- bundle
85
102
}
86
103
87
104
/// Returns an iterator for a [`FluentBundle`] for each locale provided. Each
@@ -92,24 +109,51 @@ impl ResourceManager {
92
109
& self ,
93
110
locales : Vec < LanguageIdentifier > ,
94
111
resource_ids : Vec < String > ,
95
- ) -> impl Iterator < Item = FluentBundle < & FluentResource > > {
96
- let res_mgr = self ;
112
+ ) -> impl Iterator < Item = Result < FluentBundle < & FluentResource > , Vec < ResourceManagerError > > >
113
+ {
97
114
let mut idx = 0 ;
98
115
99
116
iter:: from_fn ( move || {
100
117
locales. get ( idx) . map ( |locale| {
101
118
idx += 1 ;
119
+ let mut errors: Vec < ResourceManagerError > = vec ! [ ] ;
102
120
let mut bundle = FluentBundle :: new ( vec ! [ locale. clone( ) ] ) ;
121
+
103
122
for resource_id in & resource_ids {
104
- let resource = res_mgr. get_resource ( resource_id, & locale. to_string ( ) ) ;
105
- bundle. add_resource ( resource) . unwrap ( ) ;
123
+ match self . get_resource ( resource_id, & locale. to_string ( ) ) {
124
+ Ok ( resource) => {
125
+ if let Err ( errs) = bundle. add_resource ( resource) {
126
+ errs. into_iter ( ) . for_each ( |error| {
127
+ errors. push ( ResourceManagerError :: Fluent ( error) )
128
+ } )
129
+ }
130
+ }
131
+ Err ( error) => errors. push ( error) ,
132
+ }
133
+ }
134
+
135
+ if !errors. is_empty ( ) {
136
+ Err ( errors)
137
+ } else {
138
+ Ok ( bundle)
106
139
}
107
- bundle
108
140
} )
109
141
} )
110
142
}
111
143
}
112
144
145
+ /// Errors generated during the process of retrieving the localization resources
146
+ #[ derive( Error , Debug ) ]
147
+ pub enum ResourceManagerError {
148
+ /// Error while reading the resource file
149
+ #[ error( "{0}" ) ]
150
+ Io ( #[ from] std:: io:: Error ) ,
151
+
152
+ /// Error while trying to add a resource to the bundle
153
+ #[ error( "{0}" ) ]
154
+ Fluent ( #[ from] fluent_bundle:: FluentError ) ,
155
+ }
156
+
113
157
// Due to limitation of trait, we need a nameable Iterator type. Due to the
114
158
// lack of GATs, these have to own members instead of taking slices.
115
159
pub struct BundleIter {
@@ -184,35 +228,37 @@ mod test {
184
228
let res_mgr = ResourceManager :: new ( "./tests/resources/{locale}/{res_id}" . into ( ) ) ;
185
229
186
230
let _bundle = res_mgr. get_bundle ( vec ! [ langid!( "en-US" ) ] , vec ! [ "test.ftl" . into( ) ] ) ;
187
- let res_1 = res_mgr. get_resource ( "test.ftl" , "en-US" ) ;
231
+ let res_1 = res_mgr
232
+ . get_resource ( "test.ftl" , "en-US" )
233
+ . expect ( "Could not get resource" ) ;
188
234
189
235
let _bundle2 = res_mgr. get_bundle ( vec ! [ langid!( "en-US" ) ] , vec ! [ "test.ftl" . into( ) ] ) ;
190
- let res_2 = res_mgr. get_resource ( "test.ftl" , "en-US" ) ;
236
+ let res_2 = res_mgr
237
+ . get_resource ( "test.ftl" , "en-US" )
238
+ . expect ( "Could not get resource" ) ;
191
239
192
240
assert ! (
193
241
std:: ptr:: eq( res_1, res_2) ,
194
242
"The resources are cached in memory and reference the same thing."
195
243
) ;
196
244
}
197
245
198
- // TODO - This API should return a Result instead.
199
- // https://github.com/projectfluent/fluent-rs/issues/278
200
246
#[ test]
201
- #[ should_panic]
202
247
fn get_resource_error ( ) {
203
248
let res_mgr = ResourceManager :: new ( "./tests/resources/{locale}/{res_id}" . into ( ) ) ;
204
249
205
250
let _bundle = res_mgr. get_bundle ( vec ! [ langid!( "en-US" ) ] , vec ! [ "test.ftl" . into( ) ] ) ;
206
- res_mgr. get_resource ( "nonexistent.ftl" , "en-US" ) ;
251
+ let res = res_mgr. get_resource ( "nonexistent.ftl" , "en-US" ) ;
252
+
253
+ assert ! ( res. is_err( ) ) ;
207
254
}
208
255
209
- // TODO - This API should return a Result instead.
210
- // https://github.com/projectfluent/fluent-rs/issues/278
211
256
#[ test]
212
- #[ should_panic]
213
257
fn get_bundle_error ( ) {
214
258
let res_mgr = ResourceManager :: new ( "./tests/resources/{locale}/{res_id}" . into ( ) ) ;
215
- let _bundle = res_mgr. get_bundle ( vec ! [ langid!( "en-US" ) ] , vec ! [ "nonexistent.ftl" . into( ) ] ) ;
259
+ let bundle = res_mgr. get_bundle ( vec ! [ langid!( "en-US" ) ] , vec ! [ "nonexistent.ftl" . into( ) ] ) ;
260
+
261
+ assert ! ( bundle. is_err( ) ) ;
216
262
}
217
263
218
264
// TODO - Syntax errors should be surfaced. This test has an invalid resource that
@@ -221,22 +267,24 @@ mod test {
221
267
#[ test]
222
268
fn get_bundle_ignores_errors ( ) {
223
269
let res_mgr = ResourceManager :: new ( "./tests/resources/{locale}/{res_id}" . into ( ) ) ;
224
- let bundle = res_mgr. get_bundle (
225
- vec ! [ langid!( "en-US" ) ] ,
226
- vec ! [ "test.ftl" . into( ) , "invalid.ftl" . into( ) ] ,
227
- ) ;
270
+ let bundle = res_mgr
271
+ . get_bundle (
272
+ vec ! [ langid!( "en-US" ) ] ,
273
+ vec ! [ "test.ftl" . into( ) , "invalid.ftl" . into( ) ] ,
274
+ )
275
+ . expect ( "Could not retrieve bundle" ) ;
228
276
229
277
let mut errors = vec ! [ ] ;
230
278
let msg = bundle. get_message ( "hello-world" ) . expect ( "Message exists" ) ;
231
279
let pattern = msg. value ( ) . expect ( "Message has a value" ) ;
232
- let value = bundle. format_pattern ( & pattern, None , & mut errors) ;
280
+ let value = bundle. format_pattern ( pattern, None , & mut errors) ;
233
281
assert_eq ! ( value, "Hello World" ) ;
234
282
assert ! ( errors. is_empty( ) ) ;
235
283
236
284
let mut errors = vec ! [ ] ;
237
285
let msg = bundle. get_message ( "valid-message" ) . expect ( "Message exists" ) ;
238
286
let pattern = msg. value ( ) . expect ( "Message has a value" ) ;
239
- let value = bundle. format_pattern ( & pattern, None , & mut errors) ;
287
+ let value = bundle. format_pattern ( pattern, None , & mut errors) ;
240
288
assert_eq ! ( value, "This is a valid message" ) ;
241
289
assert ! ( errors. is_empty( ) ) ;
242
290
}
0 commit comments