11use crate :: assets:: { AssetFiles , compile_assets} ;
2- use crate :: category:: Category ;
32use crate :: fs:: ensure_directory;
43use crate :: i18n:: { EXPLICIT_LOCALE_INFO , LocaleInfo , SUPPORTED_LOCALES , TeamHelper , create_loader} ;
54use crate :: rust_version:: { RustVersion , fetch_rust_version} ;
6- use crate :: teams:: { RustTeams , load_rust_teams} ;
5+ use crate :: teams:: { PageData , RustTeams , encode_zulip_stream , load_rust_teams} ;
76use anyhow:: Context ;
87use handlebars:: { DirectorySourceOptions , Handlebars } ;
98use handlebars_fluent:: { FluentHelper , Loader , SimpleLoader } ;
109use serde:: Serialize ;
10+ use std:: ffi:: OsStr ;
1111use std:: fs:: File ;
1212use std:: io:: BufWriter ;
1313use std:: path:: { Path , PathBuf } ;
1414
1515mod assets;
16- mod category;
1716mod fs;
1817mod i18n;
1918mod rust_version;
@@ -67,7 +66,10 @@ impl<'a, T: Serialize> PageCtx<'a, T> {
6766
6867 let out_path = self . output_dir . join ( path) ;
6968 ensure_directory ( & out_path) ?;
70- let mut output_file = BufWriter :: new ( File :: create ( & out_path) ?) ;
69+ let mut output_file = BufWriter :: new (
70+ File :: create ( & out_path)
71+ . with_context ( || anyhow:: anyhow!( "Cannot create file at {}" , path. display( ) ) ) ?,
72+ ) ;
7173 eprintln ! ( "Rendering `{template}` into {}" , out_path. display( ) ) ;
7274
7375 self . handlebars
@@ -84,6 +86,7 @@ impl<'a, T: Serialize> PageCtx<'a, T> {
8486
8587struct RenderCtx < ' a > {
8688 handlebars : Handlebars < ' a > ,
89+ template_dir : PathBuf ,
8790 fluent_loader : SimpleLoader ,
8891 output_dir : PathBuf ,
8992 rust_version : RustVersion ,
@@ -124,16 +127,28 @@ impl<'a> RenderCtx<'a> {
124127 }
125128 }
126129
127- fn copy_static_assets < P : AsRef < Path > > ( & self , src_dir : P , dst_dir : P ) -> anyhow:: Result < ( ) > {
130+ fn copy_asset_dir < P : AsRef < Path > > ( & self , src_dir : P , dst_dir : P ) -> anyhow:: Result < ( ) > {
128131 let dst = self . output_dir . join ( dst_dir. as_ref ( ) ) ;
129132 println ! (
130- "Copying static assets from {} to {}" ,
133+ "Copying static asset directory from {} to {}" ,
131134 src_dir. as_ref( ) . display( ) ,
132135 dst. display( )
133136 ) ;
134137 copy_dir_all ( src_dir. as_ref ( ) , dst) ?;
135138 Ok ( ( ) )
136139 }
140+
141+ fn copy_asset_file < P : AsRef < Path > > ( & self , src : P , dst : P ) -> anyhow:: Result < ( ) > {
142+ let dst = self . output_dir . join ( dst. as_ref ( ) ) ;
143+ println ! (
144+ "Copying static asset file from {} to {}" ,
145+ src. as_ref( ) . display( ) ,
146+ dst. display( )
147+ ) ;
148+ ensure_directory ( & dst) ?;
149+ std:: fs:: copy ( src. as_ref ( ) , dst) ?;
150+ Ok ( ( ) )
151+ }
137152}
138153
139154/// Calls `func` for all supported languages.
@@ -181,6 +196,7 @@ fn setup_handlebars() -> anyhow::Result<Handlebars<'static>> {
181196 let helper = FluentHelper :: new ( loader) ;
182197 handlebars. register_helper ( "fluent" , Box :: new ( helper) ) ;
183198 handlebars. register_helper ( "team-text" , Box :: new ( TeamHelper :: new ( ) ) ) ;
199+ handlebars. register_helper ( "encode-zulip-stream" , Box :: new ( encode_zulip_stream) ) ;
184200 Ok ( handlebars)
185201}
186202
@@ -194,28 +210,35 @@ async fn main() -> anyhow::Result<()> {
194210 let _ = std:: fs:: remove_dir_all ( & output_dir) ;
195211 std:: fs:: create_dir_all ( & output_dir) ?;
196212
197- let assets = compile_assets ( Path :: new ( "." ) , & output_dir, "/" ) ?;
213+ let root_dir = Path :: new ( "." ) ;
214+ let assets = compile_assets ( root_dir, & output_dir, "/" ) ?;
198215 let handlebars = setup_handlebars ( ) ?;
199216
200217 let ctx = RenderCtx {
218+ template_dir : root_dir. join ( "templates" ) ,
201219 fluent_loader : create_loader ( ) ,
202220 assets,
203221 rust_version,
204222 teams,
205223 handlebars,
206224 output_dir,
207225 } ;
208- ctx. copy_static_assets ( "static" , "static" ) ?;
226+ ctx. copy_asset_dir ( "static" , "static" ) ?;
227+ ctx. copy_asset_file (
228+ "static/text/well_known_security.txt" ,
229+ ".well-known/security.txt" ,
230+ ) ?;
209231
210232 render_index ( & ctx) ?;
211233 render_governance ( & ctx) ?;
212- render_category ( & ctx, "community" ) ?;
213- render_category ( & ctx, "learn" ) ?;
214- render_category ( & ctx, "policies" ) ?;
215- render_category ( & ctx, "tools" ) ?;
216- render_category ( & ctx, "what" ) ?;
234+ render_directory ( & ctx, "community" ) ?;
235+ render_directory ( & ctx, "learn" ) ?;
236+ render_directory ( & ctx, "policies" ) ?;
237+ render_directory ( & ctx, "tools" ) ?;
238+ render_directory ( & ctx, "what" ) ?;
239+ ctx. page ( "404" , "" , & ( ) , ENGLISH ) . render ( "404.html" ) ?;
217240
218- // TODO: 404, redirects
241+ // TODO: redirects
219242
220243 Ok ( ( ) )
221244}
@@ -243,18 +266,78 @@ fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
243266 render_ctx
244267 . page ( "governance/index" , "governance-page-title" , & data, lang)
245268 . render ( dst_path)
246- } )
269+ } ) ?;
270+ for team in data. teams {
271+ let data: PageData = render_ctx
272+ . teams
273+ . page_data ( team. section , & team. page_name )
274+ . unwrap_or_else ( |error| panic ! ( "Page data for team {team:?} not found: {error:?}" ) ) ;
275+
276+ // We need to render into index.html to have an extensionless URL
277+ all_langs (
278+ & format ! ( "governance/{}/index.html" , team. url) ,
279+ |dst_path, lang| {
280+ render_ctx
281+ . page (
282+ "governance/group" ,
283+ & format ! ( "governance-team-{}-title" , team. team. name) ,
284+ & data,
285+ lang,
286+ )
287+ . render ( dst_path)
288+ } ,
289+ ) ?;
290+ }
291+
292+ Ok ( ( ) )
247293}
248294
249- fn render_category ( render_ctx : & RenderCtx , category : & str ) -> anyhow:: Result < ( ) > {
250- all_langs ( & format ! ( "{category}/index.html" ) , |dst_path, lang| {
251- render_ctx
252- . page (
253- & format ! ( "{category}/index" ) ,
254- & format ! ( "{category}-page-title" ) ,
255- & ( ) ,
256- lang,
257- )
258- . render ( dst_path)
259- } )
295+ /// Render all templates found in the given directory.
296+ fn render_directory ( render_ctx : & RenderCtx , category : & str ) -> anyhow:: Result < ( ) > {
297+ for dir in std:: fs:: read_dir ( render_ctx. template_dir . join ( category) ) ? {
298+ let path = dir?. path ( ) ;
299+ if path. is_file ( ) && path. extension ( ) == Some ( OsStr :: new ( "hbs" ) ) {
300+ // foo.html.hbs => foo
301+ let subject = path
302+ . file_stem ( )
303+ . unwrap ( )
304+ . to_str ( )
305+ . unwrap ( )
306+ . split ( "." )
307+ . next ( )
308+ . unwrap ( ) ;
309+
310+ // The "root" page of a category
311+ if subject == "index" {
312+ all_langs ( & format ! ( "{category}/index.html" ) , |dst_path, lang| {
313+ render_ctx
314+ . page (
315+ & format ! ( "{category}/index" ) ,
316+ & format ! ( "{category}-page-title" ) ,
317+ & ( ) ,
318+ lang,
319+ )
320+ . render ( dst_path)
321+ } ) ?;
322+ } else {
323+ // A subpage (subject) of the category
324+ // We need to render the page into a subdirectory, so that /foo/bar works without
325+ // needing a HTML suffix.
326+ all_langs (
327+ & format ! ( "{category}/{subject}/index.html" ) ,
328+ |dst_path, lang| {
329+ render_ctx
330+ . page (
331+ & format ! ( "{category}/{subject}" ) ,
332+ & format ! ( "{category}-{subject}-page-title" ) ,
333+ & ( ) ,
334+ lang,
335+ )
336+ . render ( dst_path)
337+ } ,
338+ ) ?;
339+ }
340+ }
341+ }
342+ Ok ( ( ) )
260343}
0 commit comments