1
1
use content:: { Library , Taxonomy , TaxonomyTerm } ;
2
2
use libs:: tera:: { from_value, to_value, Function as TeraFn , Result , Value } ;
3
+ use std:: borrow:: Cow ;
3
4
use std:: collections:: HashMap ;
4
5
use std:: path:: PathBuf ;
5
6
use std:: sync:: { Arc , RwLock } ;
@@ -71,14 +72,60 @@ impl TeraFn for GetTaxonomyUrl {
71
72
}
72
73
}
73
74
75
+ fn add_lang_to_path < ' a > ( path : & str , lang : & str ) -> Result < Cow < ' a , String > > {
76
+ match path. rfind ( '.' ) {
77
+ Some ( period_offset) => {
78
+ let prefix = path. get ( 0 ..period_offset) ;
79
+ let suffix = path. get ( period_offset..) ;
80
+ if prefix. is_none ( ) || suffix. is_none ( ) {
81
+ Err ( format ! ( "Error adding language code to {}" , path) . into ( ) )
82
+ } else {
83
+ Ok ( Cow :: Owned ( format ! ( "{}.{}{}" , prefix. unwrap( ) , lang, suffix. unwrap( ) ) ) )
84
+ }
85
+ }
86
+ None => Ok ( Cow :: Owned ( format ! ( "{}.{}" , path, lang) ) ) ,
87
+ }
88
+ }
89
+
90
+ fn get_path_with_lang < ' a > (
91
+ path : & ' a String ,
92
+ lang : & Option < String > ,
93
+ default_lang : & str ,
94
+ supported_languages : & [ String ] ,
95
+ ) -> Result < Cow < ' a , String > > {
96
+ if supported_languages. contains ( & default_lang. to_string ( ) ) {
97
+ lang. as_ref ( ) . map_or_else (
98
+ || Ok ( Cow :: Borrowed ( path) ) ,
99
+ |lang_code| match default_lang == lang_code {
100
+ true => Ok ( Cow :: Borrowed ( path) ) ,
101
+ false => add_lang_to_path ( path, lang_code) ,
102
+ } ,
103
+ )
104
+ } else {
105
+ Err ( format ! ( "Unsupported language {}" , default_lang) . into ( ) )
106
+ }
107
+ }
108
+
74
109
#[ derive( Debug ) ]
75
110
pub struct GetPage {
76
111
base_path : PathBuf ,
112
+ default_lang : String ,
113
+ supported_languages : Arc < Vec < String > > ,
77
114
library : Arc < RwLock < Library > > ,
78
115
}
79
116
impl GetPage {
80
- pub fn new ( base_path : PathBuf , library : Arc < RwLock < Library > > ) -> Self {
81
- Self { base_path : base_path. join ( "content" ) , library }
117
+ pub fn new (
118
+ base_path : PathBuf ,
119
+ default_lang : & str ,
120
+ supported_languages : Arc < Vec < String > > ,
121
+ library : Arc < RwLock < Library > > ,
122
+ ) -> Self {
123
+ Self {
124
+ base_path : base_path. join ( "content" ) ,
125
+ default_lang : default_lang. to_string ( ) ,
126
+ supported_languages,
127
+ library,
128
+ }
82
129
}
83
130
}
84
131
impl TeraFn for GetPage {
@@ -88,23 +135,50 @@ impl TeraFn for GetPage {
88
135
args. get( "path" ) ,
89
136
"`get_page` requires a `path` argument with a string value"
90
137
) ;
91
- let full_path = self . base_path . join ( & path) ;
92
- let library = self . library . read ( ) . unwrap ( ) ;
93
- match library. pages . get ( & full_path) {
94
- Some ( p) => Ok ( to_value ( p. serialize ( & library) ) . unwrap ( ) ) ,
95
- None => Err ( format ! ( "Page `{}` not found." , path) . into ( ) ) ,
96
- }
138
+
139
+ let lang =
140
+ optional_arg ! ( String , args. get( "lang" ) , "`get_section`: `lang` must be a string" ) ;
141
+
142
+ get_path_with_lang ( & path, & lang, & self . default_lang , & self . supported_languages ) . and_then (
143
+ |path_with_lang| {
144
+ let full_path = self . base_path . join ( path_with_lang. as_ref ( ) ) ;
145
+ let library = self . library . read ( ) . unwrap ( ) ;
146
+
147
+ match library. pages . get ( & full_path) {
148
+ Some ( p) => Ok ( to_value ( p. serialize ( & library) ) . unwrap ( ) ) ,
149
+ None => match lang {
150
+ Some ( lang_code) => {
151
+ Err ( format ! ( "Page `{}` not found for language `{}`." , path, lang_code)
152
+ . into ( ) )
153
+ }
154
+ None => Err ( format ! ( "Page `{}` not found." , path) . into ( ) ) ,
155
+ } ,
156
+ }
157
+ } ,
158
+ )
97
159
}
98
160
}
99
161
100
162
#[ derive( Debug ) ]
101
163
pub struct GetSection {
102
164
base_path : PathBuf ,
165
+ default_lang : String ,
166
+ supported_languages : Arc < Vec < String > > ,
103
167
library : Arc < RwLock < Library > > ,
104
168
}
105
169
impl GetSection {
106
- pub fn new ( base_path : PathBuf , library : Arc < RwLock < Library > > ) -> Self {
107
- Self { base_path : base_path. join ( "content" ) , library }
170
+ pub fn new (
171
+ base_path : PathBuf ,
172
+ default_lang : & str ,
173
+ supported_languages : Arc < Vec < String > > ,
174
+ library : Arc < RwLock < Library > > ,
175
+ ) -> Self {
176
+ Self {
177
+ base_path : base_path. join ( "content" ) ,
178
+ default_lang : default_lang. to_string ( ) ,
179
+ supported_languages,
180
+ library,
181
+ }
108
182
}
109
183
}
110
184
impl TeraFn for GetSection {
@@ -119,19 +193,32 @@ impl TeraFn for GetSection {
119
193
. get ( "metadata_only" )
120
194
. map_or ( false , |c| from_value :: < bool > ( c. clone ( ) ) . unwrap_or ( false ) ) ;
121
195
122
- let full_path = self . base_path . join ( & path) ;
123
- let library = self . library . read ( ) . unwrap ( ) ;
124
-
125
- match library. sections . get ( & full_path) {
126
- Some ( s) => {
127
- if metadata_only {
128
- Ok ( to_value ( s. serialize_basic ( & library) ) . unwrap ( ) )
129
- } else {
130
- Ok ( to_value ( s. serialize ( & library) ) . unwrap ( ) )
196
+ let lang =
197
+ optional_arg ! ( String , args. get( "lang" ) , "`get_section`: `lang` must be a string" ) ;
198
+
199
+ get_path_with_lang ( & path, & lang, self . default_lang . as_str ( ) , & self . supported_languages )
200
+ . and_then ( |path_with_lang| {
201
+ let full_path = self . base_path . join ( path_with_lang. as_ref ( ) ) ;
202
+ let library = self . library . read ( ) . unwrap ( ) ;
203
+
204
+ match library. sections . get ( & full_path) {
205
+ Some ( s) => {
206
+ if metadata_only {
207
+ Ok ( to_value ( s. serialize_basic ( & library) ) . unwrap ( ) )
208
+ } else {
209
+ Ok ( to_value ( s. serialize ( & library) ) . unwrap ( ) )
210
+ }
211
+ }
212
+ None => match lang {
213
+ Some ( lang_code) => Err ( format ! (
214
+ "Section `{}` not found for language `{}`." ,
215
+ path, lang_code
216
+ )
217
+ . into ( ) ) ,
218
+ None => Err ( format ! ( "Section `{}` not found." , path) . into ( ) ) ,
219
+ } ,
131
220
}
132
- }
133
- None => Err ( format ! ( "Section `{}` not found." , path) . into ( ) ) ,
134
- }
221
+ } )
135
222
}
136
223
}
137
224
@@ -273,7 +360,148 @@ impl TeraFn for GetTaxonomyTerm {
273
360
mod tests {
274
361
use super :: * ;
275
362
use config:: { Config , TaxonomyConfig } ;
276
- use content:: TaxonomyTerm ;
363
+ use content:: { FileInfo , Library , Page , Section , SortBy , TaxonomyTerm } ;
364
+ use std:: path:: Path ;
365
+ use std:: sync:: { Arc , RwLock } ;
366
+
367
+ fn create_page ( title : & str , file_path : & str , lang : & str ) -> Page {
368
+ let mut page = Page { lang : lang. to_owned ( ) , ..Page :: default ( ) } ;
369
+ page. file = FileInfo :: new_page (
370
+ Path :: new ( format ! ( "/test/base/path/{}" , file_path) . as_str ( ) ) ,
371
+ & PathBuf :: new ( ) ,
372
+ ) ;
373
+ page. meta . title = Some ( title. to_string ( ) ) ;
374
+ page. meta . weight = Some ( 1 ) ;
375
+ page. file . find_language ( "en" , & [ "fr" ] ) . unwrap ( ) ;
376
+ page
377
+ }
378
+
379
+ #[ test]
380
+ fn can_get_page ( ) {
381
+ let mut library = Library :: default ( ) ;
382
+ let pages = vec ! [
383
+ ( "Homepage" , "content/homepage.md" , "en" ) ,
384
+ ( "Page D'Accueil" , "content/homepage.fr.md" , "fr" ) ,
385
+ ( "Blog" , "content/blog.md" , "en" ) ,
386
+ ( "Wiki" , "content/wiki.md" , "en" ) ,
387
+ ( "Wiki" , "content/wiki.fr.md" , "fr" ) ,
388
+ ( "Recipes" , "content/wiki/recipes.md" , "en" ) ,
389
+ ( "Recettes" , "content/wiki/recipes.fr.md" , "fr" ) ,
390
+ ( "Programming" , "content/wiki/programming.md" , "en" ) ,
391
+ ( "La Programmation" , "content/wiki/programming.fr.md" , "fr" ) ,
392
+ ( "Novels" , "content/novels.md" , "en" ) ,
393
+ ( "Des Romans" , "content/novels.fr.md" , "fr" ) ,
394
+ ] ;
395
+ for ( t, f, l) in pages. clone ( ) {
396
+ library. insert_page ( create_page ( t, f, l) ) ;
397
+ }
398
+ let base_path = "/test/base/path" . into ( ) ;
399
+ let lang_list = vec ! [ "en" . to_string( ) , "fr" . to_string( ) ] ;
400
+
401
+ let static_fn =
402
+ GetPage :: new ( base_path, "en" , Arc :: new ( lang_list) , Arc :: new ( RwLock :: new ( library) ) ) ;
403
+
404
+ // Find with lang argument
405
+ let mut args = HashMap :: new ( ) ;
406
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes.md" ) . unwrap ( ) ) ;
407
+ args. insert ( "lang" . to_string ( ) , to_value ( "fr" ) . unwrap ( ) ) ;
408
+ let res = static_fn. call ( & args) . unwrap ( ) ;
409
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
410
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recettes" ) . unwrap( ) ) ;
411
+
412
+ // Find with lang in path for legacy support
413
+ args = HashMap :: new ( ) ;
414
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes.fr.md" ) . unwrap ( ) ) ;
415
+ let res = static_fn. call ( & args) . unwrap ( ) ;
416
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
417
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recettes" ) . unwrap( ) ) ;
418
+
419
+ // Find with default lang
420
+ args = HashMap :: new ( ) ;
421
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes.md" ) . unwrap ( ) ) ;
422
+ let res = static_fn. call ( & args) . unwrap ( ) ;
423
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
424
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recipes" ) . unwrap( ) ) ;
425
+
426
+ // Find with default lang when default lang passed
427
+ args = HashMap :: new ( ) ;
428
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes.md" ) . unwrap ( ) ) ;
429
+ args. insert ( "lang" . to_string ( ) , to_value ( "en" ) . unwrap ( ) ) ;
430
+ let res = static_fn. call ( & args) . unwrap ( ) ;
431
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
432
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recipes" ) . unwrap( ) ) ;
433
+ }
434
+
435
+ fn create_section ( title : & str , file_path : & str , lang : & str ) -> Section {
436
+ let mut section = Section { lang : lang. to_owned ( ) , ..Section :: default ( ) } ;
437
+ section. file = FileInfo :: new_section (
438
+ Path :: new ( format ! ( "/test/base/path/{}" , file_path) . as_str ( ) ) ,
439
+ & PathBuf :: new ( ) ,
440
+ ) ;
441
+ section. meta . title = Some ( title. to_string ( ) ) ;
442
+ section. meta . weight = 1 ;
443
+ section. meta . transparent = false ;
444
+ section. meta . sort_by = SortBy :: None ;
445
+ section. meta . page_template = Some ( "new_page.html" . to_owned ( ) ) ;
446
+ section. file . find_language ( "en" , & [ "fr" ] ) . unwrap ( ) ;
447
+ section
448
+ }
449
+
450
+ #[ test]
451
+ fn can_get_section ( ) {
452
+ let mut library = Library :: default ( ) ;
453
+ let sections = vec ! [
454
+ ( "Homepage" , "content/_index.md" , "en" ) ,
455
+ ( "Page D'Accueil" , "content/_index.fr.md" , "fr" ) ,
456
+ ( "Blog" , "content/blog/_index.md" , "en" ) ,
457
+ ( "Wiki" , "content/wiki/_index.md" , "en" ) ,
458
+ ( "Wiki" , "content/wiki/_index.fr.md" , "fr" ) ,
459
+ ( "Recipes" , "content/wiki/recipes/_index.md" , "en" ) ,
460
+ ( "Recettes" , "content/wiki/recipes/_index.fr.md" , "fr" ) ,
461
+ ( "Programming" , "content/wiki/programming/_index.md" , "en" ) ,
462
+ ( "La Programmation" , "content/wiki/programming/_index.fr.md" , "fr" ) ,
463
+ ( "Novels" , "content/novels/_index.md" , "en" ) ,
464
+ ( "Des Romans" , "content/novels/_index.fr.md" , "fr" ) ,
465
+ ] ;
466
+ for ( t, f, l) in sections. clone ( ) {
467
+ library. insert_section ( create_section ( t, f, l) ) ;
468
+ }
469
+ let base_path = "/test/base/path" . into ( ) ;
470
+ let lang_list = vec ! [ "en" . to_string( ) , "fr" . to_string( ) ] ;
471
+
472
+ let static_fn =
473
+ GetSection :: new ( base_path, "en" , Arc :: new ( lang_list) , Arc :: new ( RwLock :: new ( library) ) ) ;
474
+
475
+ // Find with lang argument
476
+ let mut args = HashMap :: new ( ) ;
477
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes/_index.md" ) . unwrap ( ) ) ;
478
+ args. insert ( "lang" . to_string ( ) , to_value ( "fr" ) . unwrap ( ) ) ;
479
+ let res = static_fn. call ( & args) . unwrap ( ) ;
480
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
481
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recettes" ) . unwrap( ) ) ;
482
+
483
+ // Find with lang in path for legacy support
484
+ args = HashMap :: new ( ) ;
485
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes/_index.fr.md" ) . unwrap ( ) ) ;
486
+ let res = static_fn. call ( & args) . unwrap ( ) ;
487
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
488
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recettes" ) . unwrap( ) ) ;
489
+
490
+ // Find with default lang
491
+ args = HashMap :: new ( ) ;
492
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes/_index.md" ) . unwrap ( ) ) ;
493
+ let res = static_fn. call ( & args) . unwrap ( ) ;
494
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
495
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recipes" ) . unwrap( ) ) ;
496
+
497
+ // Find with default lang when default lang passed
498
+ args = HashMap :: new ( ) ;
499
+ args. insert ( "path" . to_string ( ) , to_value ( "wiki/recipes/_index.md" ) . unwrap ( ) ) ;
500
+ args. insert ( "lang" . to_string ( ) , to_value ( "en" ) . unwrap ( ) ) ;
501
+ let res = static_fn. call ( & args) . unwrap ( ) ;
502
+ let res_obj = res. as_object ( ) . unwrap ( ) ;
503
+ assert_eq ! ( res_obj[ "title" ] , to_value( "Recipes" ) . unwrap( ) ) ;
504
+ }
277
505
278
506
#[ test]
279
507
fn can_get_taxonomy ( ) {
0 commit comments