11use content:: { Library , Taxonomy , TaxonomyTerm } ;
22use libs:: tera:: { from_value, to_value, Function as TeraFn , Result , Value } ;
3+ use std:: borrow:: Cow ;
34use std:: collections:: HashMap ;
45use std:: path:: PathBuf ;
56use std:: sync:: { Arc , RwLock } ;
@@ -71,14 +72,60 @@ impl TeraFn for GetTaxonomyUrl {
7172 }
7273}
7374
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+
74109#[ derive( Debug ) ]
75110pub struct GetPage {
76111 base_path : PathBuf ,
112+ default_lang : String ,
113+ supported_languages : Arc < Vec < String > > ,
77114 library : Arc < RwLock < Library > > ,
78115}
79116impl 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+ }
82129 }
83130}
84131impl TeraFn for GetPage {
@@ -88,23 +135,50 @@ impl TeraFn for GetPage {
88135 args. get( "path" ) ,
89136 "`get_page` requires a `path` argument with a string value"
90137 ) ;
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+ )
97159 }
98160}
99161
100162#[ derive( Debug ) ]
101163pub struct GetSection {
102164 base_path : PathBuf ,
165+ default_lang : String ,
166+ supported_languages : Arc < Vec < String > > ,
103167 library : Arc < RwLock < Library > > ,
104168}
105169impl 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+ }
108182 }
109183}
110184impl TeraFn for GetSection {
@@ -119,19 +193,32 @@ impl TeraFn for GetSection {
119193 . get ( "metadata_only" )
120194 . map_or ( false , |c| from_value :: < bool > ( c. clone ( ) ) . unwrap_or ( false ) ) ;
121195
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+ } ,
131220 }
132- }
133- None => Err ( format ! ( "Section `{}` not found." , path) . into ( ) ) ,
134- }
221+ } )
135222 }
136223}
137224
@@ -273,7 +360,148 @@ impl TeraFn for GetTaxonomyTerm {
273360mod tests {
274361 use super :: * ;
275362 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+ }
277505
278506 #[ test]
279507 fn can_get_taxonomy ( ) {
0 commit comments