@@ -6,9 +6,9 @@ use fluent_fallback::{
66} ;
77use futures:: stream:: Stream ;
88use rustc_hash:: FxHashSet ;
9- use std:: fs;
109use std:: io;
11- use std:: iter;
10+ use std:: { fs, iter} ;
11+ use thiserror:: Error ;
1212use unic_langid:: LanguageIdentifier ;
1313
1414fn read_file ( path : & str ) -> Result < String , io:: Error > {
@@ -48,21 +48,24 @@ impl ResourceManager {
4848
4949 /// Returns a [`FluentResource`], by either reading the file and loading it into
5050 /// 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 > {
5256 let path = self
5357 . path_scheme
5458 . 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
5862 } 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,
6366 } ;
64- self . resources . insert ( path. to_string ( ) , Box :: new ( res ) )
65- }
67+ self . resources . insert ( path. to_string ( ) , Box :: new ( resource ) )
68+ } )
6669 }
6770
6871 /// Gets a [`FluentBundle`] from a list of resources. The bundle will only contain the
@@ -74,14 +77,28 @@ impl ResourceManager {
7477 & self ,
7578 locales : Vec < LanguageIdentifier > ,
7679 resource_ids : Vec < String > ,
77- ) -> FluentBundle < & FluentResource > {
80+ ) -> Result < FluentBundle < & FluentResource > , Vec < ResourceManagerError > > {
81+ let mut errors: Vec < ResourceManagerError > = vec ! [ ] ;
7882 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)
83101 }
84- bundle
85102 }
86103
87104 /// Returns an iterator for a [`FluentBundle`] for each locale provided. Each
@@ -92,24 +109,51 @@ impl ResourceManager {
92109 & self ,
93110 locales : Vec < LanguageIdentifier > ,
94111 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+ {
97114 let mut idx = 0 ;
98115
99116 iter:: from_fn ( move || {
100117 locales. get ( idx) . map ( |locale| {
101118 idx += 1 ;
119+ let mut errors: Vec < ResourceManagerError > = vec ! [ ] ;
102120 let mut bundle = FluentBundle :: new ( vec ! [ locale. clone( ) ] ) ;
121+
103122 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)
106139 }
107- bundle
108140 } )
109141 } )
110142 }
111143}
112144
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+
113157// Due to limitation of trait, we need a nameable Iterator type. Due to the
114158// lack of GATs, these have to own members instead of taking slices.
115159pub struct BundleIter {
@@ -184,35 +228,37 @@ mod test {
184228 let res_mgr = ResourceManager :: new ( "./tests/resources/{locale}/{res_id}" . into ( ) ) ;
185229
186230 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" ) ;
188234
189235 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" ) ;
191239
192240 assert ! (
193241 std:: ptr:: eq( res_1, res_2) ,
194242 "The resources are cached in memory and reference the same thing."
195243 ) ;
196244 }
197245
198- // TODO - This API should return a Result instead.
199- // https://github.com/projectfluent/fluent-rs/issues/278
200246 #[ test]
201- #[ should_panic]
202247 fn get_resource_error ( ) {
203248 let res_mgr = ResourceManager :: new ( "./tests/resources/{locale}/{res_id}" . into ( ) ) ;
204249
205250 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( ) ) ;
207254 }
208255
209- // TODO - This API should return a Result instead.
210- // https://github.com/projectfluent/fluent-rs/issues/278
211256 #[ test]
212- #[ should_panic]
213257 fn get_bundle_error ( ) {
214258 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( ) ) ;
216262 }
217263
218264 // TODO - Syntax errors should be surfaced. This test has an invalid resource that
@@ -221,22 +267,24 @@ mod test {
221267 #[ test]
222268 fn get_bundle_ignores_errors ( ) {
223269 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" ) ;
228276
229277 let mut errors = vec ! [ ] ;
230278 let msg = bundle. get_message ( "hello-world" ) . expect ( "Message exists" ) ;
231279 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) ;
233281 assert_eq ! ( value, "Hello World" ) ;
234282 assert ! ( errors. is_empty( ) ) ;
235283
236284 let mut errors = vec ! [ ] ;
237285 let msg = bundle. get_message ( "valid-message" ) . expect ( "Message exists" ) ;
238286 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) ;
240288 assert_eq ! ( value, "This is a valid message" ) ;
241289 assert ! ( errors. is_empty( ) ) ;
242290 }
0 commit comments