@@ -5,45 +5,172 @@ use crate::custom_component_renderer::error::Result;
55use scraper:: { Html , Selector } ;
66use std:: fs;
77use std:: io:: { Read , Write } ;
8- use std:: path:: Path ;
8+ use std:: path:: { Path , PathBuf } ;
9+ use std:: process:: Command ;
910
1011use std:: collections:: BTreeMap ;
1112
1213use serde:: Deserialize ;
1314
15+ /// Configuration specific to the i18n-helpers component.
1416#[ derive( Deserialize , Debug ) ]
15- pub struct LanguagesConfiguration {
17+ pub struct I18nConfiguration {
1618 pub languages : BTreeMap < String , String > ,
19+ pub default_language : Option < String > ,
20+ }
21+
22+ /// Configuration from the book.toml file.
23+ pub struct RendererConfiguration {
24+ pub i18n : I18nConfiguration ,
25+ pub destination : PathBuf ,
26+ pub current_language : Option < String > ,
27+ pub root : PathBuf ,
28+ }
29+
30+ impl RendererConfiguration {
31+ pub fn new (
32+ i18n : I18nConfiguration ,
33+ destination : PathBuf ,
34+ current_language : Option < String > ,
35+ root : PathBuf ,
36+ ) -> Self {
37+ RendererConfiguration {
38+ i18n,
39+ destination,
40+ current_language,
41+ root,
42+ }
43+ }
44+ }
45+
46+ pub struct RenderingContext {
47+ pub path : PathBuf ,
48+ pub destination : PathBuf ,
49+ pub language : String ,
50+ }
51+
52+ impl RenderingContext {
53+ fn new ( path : PathBuf , destination : PathBuf , language : String ) -> Self {
54+ RenderingContext {
55+ path,
56+ destination,
57+ language,
58+ }
59+ }
60+
61+ pub fn rendered_path ( & self ) -> String {
62+ let mut relative_path = PathBuf :: from (
63+ self . path
64+ . strip_prefix ( & self . destination )
65+ . expect ( "Invalid path" ) ,
66+ ) ;
67+ if let Ok ( stripped) = relative_path. strip_prefix ( & self . language ) {
68+ relative_path = stripped. to_owned ( ) ;
69+ }
70+ String :: from (
71+ relative_path
72+ . to_str ( )
73+ . expect ( "Failed to convert path to rendered path" ) ,
74+ )
75+ }
1776}
1877
1978pub ( crate ) struct BookDirectoryRenderer {
20- config : LanguagesConfiguration ,
79+ config : RendererConfiguration ,
2180 components : Vec < Box < dyn Component > > ,
81+ languages_paths : BTreeMap < String , PathBuf > ,
2282}
2383
2484impl BookDirectoryRenderer {
25- pub ( crate ) fn new ( config : LanguagesConfiguration ) -> BookDirectoryRenderer {
85+ pub ( crate ) fn new ( config : RendererConfiguration ) -> BookDirectoryRenderer {
86+ let default_language = config. i18n . default_language . clone ( ) ;
87+ let languages_paths = config
88+ . i18n
89+ . languages
90+ . keys ( )
91+ . filter ( |language| {
92+ default_language. is_none ( ) || * language != default_language. as_ref ( ) . unwrap ( )
93+ } )
94+ . map ( |language| {
95+ (
96+ language. clone ( ) ,
97+ config. destination . join ( "html" ) . join ( language) ,
98+ )
99+ } )
100+ . collect :: < BTreeMap < String , PathBuf > > ( ) ;
26101 BookDirectoryRenderer {
27102 config,
103+ languages_paths,
28104 components : Vec :: new ( ) ,
29105 }
30106 }
31107
32- pub ( crate ) fn render_book ( & mut self , path : & Path ) -> Result < ( ) > {
33- if !path. is_dir ( ) {
108+ pub fn translate ( & self ) -> Result < ( ) > {
109+ let default_language = & self . config . i18n . default_language ;
110+ for ( identifier, _) in & self . config . i18n . languages {
111+ if let Some ( default_language) = default_language {
112+ if default_language == identifier {
113+ continue ;
114+ }
115+ }
116+
117+ let destination = self . config . destination . as_path ( ) . display ( ) . to_string ( ) ;
118+ let book_folder = self . config . root . as_path ( ) . display ( ) . to_string ( ) ;
119+
120+ Command :: new ( "mdbook" )
121+ . arg ( "build" )
122+ . arg ( & book_folder)
123+ . arg ( "-d" )
124+ . arg ( & format ! ( "{destination}/{identifier}" ) )
125+ . env ( "MDBOOK_BOOK__LANGUAGE" , identifier)
126+ . env (
127+ "MDBOOK_OUTPUT__HTML__SITE_URL" ,
128+ & format ! ( "/comprehensive-rust/{identifier}/" ) ,
129+ )
130+ . output ( ) ?;
131+
132+ std:: fs:: rename (
133+ & format ! ( "{destination}/{identifier}/html" ) ,
134+ & format ! ( "{destination}/html/{identifier}" ) ,
135+ ) ?;
136+ }
137+
138+ Ok ( ( ) )
139+ }
140+
141+ pub ( crate ) fn render_book ( & mut self ) -> Result < ( ) > {
142+ if !self . config . destination . is_dir ( ) {
34143 return Err ( RendererError :: InvalidPath ( format ! (
35144 "{:?} is not a directory" ,
36- path
145+ self . config . destination
37146 ) ) ) ;
38147 }
39- self . render_book_directory ( path )
148+ self . render_book_directory ( & self . config . destination . clone ( ) )
40149 }
41150
42151 pub ( crate ) fn add_component ( & mut self , component : Box < dyn Component > ) {
43152 self . components . push ( component) ;
44153 }
45154
46- fn render_components ( & mut self , file_content : & str ) -> Result < String > {
155+ fn extract_language_from_path ( & self , path : & Path ) -> String {
156+ for ( language, language_path) in & self . languages_paths {
157+ if path. starts_with ( language_path) {
158+ return language. clone ( ) ;
159+ }
160+ }
161+ self . config
162+ . i18n
163+ . default_language
164+ . clone ( )
165+ . unwrap_or_default ( )
166+ }
167+
168+ fn render_components ( & mut self , file_content : & str , path : & Path ) -> Result < String > {
169+ let path_buf = path. to_owned ( ) ;
170+ let destination = self . config . destination . join ( "html" ) ;
171+ let language = self . extract_language_from_path ( & path_buf) ;
172+
173+ let rendering_context = RenderingContext :: new ( path_buf, destination, language) ;
47174 let mut document = Html :: parse_document ( file_content) ;
48175 for custom_component in & mut self . components {
49176 let mut node_ids = Vec :: new ( ) ;
@@ -56,7 +183,7 @@ impl BookDirectoryRenderer {
56183 let tree = & mut document. tree ;
57184 for id in node_ids {
58185 let dom_manipulator = NodeManipulator :: new ( tree, id) ;
59- custom_component. render ( dom_manipulator, & self . config ) ?;
186+ custom_component. render ( dom_manipulator, & self . config , & rendering_context ) ?;
60187 }
61188 }
62189 Ok ( document. html ( ) )
@@ -71,7 +198,7 @@ impl BookDirectoryRenderer {
71198 let mut file = fs:: File :: open ( path) ?;
72199 file. read_to_string ( & mut file_content) ?;
73200 }
74- let output_html = self . render_components ( & file_content) ?;
201+ let output_html = self . render_components ( & file_content, path ) ?;
75202 let mut file = fs:: File :: create ( path) ?;
76203 file. write_all ( output_html. as_bytes ( ) ) ?;
77204 Ok ( ( ) )
@@ -109,14 +236,20 @@ mod tests {
109236 let mut languages = BTreeMap :: new ( ) ;
110237 languages. insert ( String :: from ( "en" ) , String :: from ( "English" ) ) ;
111238 languages. insert ( String :: from ( "fr" ) , String :: from ( "French" ) ) ;
112- let mock_config = LanguagesConfiguration { languages } ;
239+ let mock_config = RendererConfiguration :: new (
240+ I18nConfiguration {
241+ languages,
242+ default_language : Some ( String :: from ( "en" ) ) ,
243+ } ,
244+ dir. path ( ) . to_owned ( ) ,
245+ Some ( String :: from ( "en" ) ) ,
246+ dir. path ( ) . to_owned ( ) ,
247+ ) ;
113248
114249 let mut renderer = BookDirectoryRenderer :: new ( mock_config) ;
115250 let test_component = Box :: new ( TestComponent :: new ( ) ) ;
116251 renderer. add_component ( test_component) ;
117- renderer
118- . render_book ( dir. path ( ) )
119- . expect ( "Failed to render book" ) ;
252+ renderer. render_book ( ) . expect ( "Failed to render book" ) ;
120253
121254 let mut output = String :: new ( ) ;
122255 let mut file = File :: open ( dir. path ( ) . join ( "test.html" ) ) . unwrap ( ) ;
0 commit comments