1
1
use crate :: assets:: { AssetFiles , compile_assets} ;
2
- use crate :: category:: Category ;
3
2
use crate :: fs:: ensure_directory;
4
3
use crate :: i18n:: { EXPLICIT_LOCALE_INFO , LocaleInfo , SUPPORTED_LOCALES , TeamHelper , create_loader} ;
5
4
use 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} ;
7
6
use anyhow:: Context ;
8
7
use handlebars:: { DirectorySourceOptions , Handlebars } ;
9
8
use handlebars_fluent:: { FluentHelper , Loader , SimpleLoader } ;
10
9
use serde:: Serialize ;
10
+ use std:: ffi:: OsStr ;
11
11
use std:: fs:: File ;
12
12
use std:: io:: BufWriter ;
13
13
use std:: path:: { Path , PathBuf } ;
14
14
15
15
mod assets;
16
- mod category;
17
16
mod fs;
18
17
mod i18n;
19
18
mod rust_version;
@@ -67,7 +66,10 @@ impl<'a, T: Serialize> PageCtx<'a, T> {
67
66
68
67
let out_path = self . output_dir . join ( path) ;
69
68
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
+ ) ;
71
73
eprintln ! ( "Rendering `{template}` into {}" , out_path. display( ) ) ;
72
74
73
75
self . handlebars
@@ -84,6 +86,7 @@ impl<'a, T: Serialize> PageCtx<'a, T> {
84
86
85
87
struct RenderCtx < ' a > {
86
88
handlebars : Handlebars < ' a > ,
89
+ template_dir : PathBuf ,
87
90
fluent_loader : SimpleLoader ,
88
91
output_dir : PathBuf ,
89
92
rust_version : RustVersion ,
@@ -124,16 +127,28 @@ impl<'a> RenderCtx<'a> {
124
127
}
125
128
}
126
129
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 < ( ) > {
128
131
let dst = self . output_dir . join ( dst_dir. as_ref ( ) ) ;
129
132
println ! (
130
- "Copying static assets from {} to {}" ,
133
+ "Copying static asset directory from {} to {}" ,
131
134
src_dir. as_ref( ) . display( ) ,
132
135
dst. display( )
133
136
) ;
134
137
copy_dir_all ( src_dir. as_ref ( ) , dst) ?;
135
138
Ok ( ( ) )
136
139
}
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
+ }
137
152
}
138
153
139
154
/// Calls `func` for all supported languages.
@@ -181,6 +196,7 @@ fn setup_handlebars() -> anyhow::Result<Handlebars<'static>> {
181
196
let helper = FluentHelper :: new ( loader) ;
182
197
handlebars. register_helper ( "fluent" , Box :: new ( helper) ) ;
183
198
handlebars. register_helper ( "team-text" , Box :: new ( TeamHelper :: new ( ) ) ) ;
199
+ handlebars. register_helper ( "encode-zulip-stream" , Box :: new ( encode_zulip_stream) ) ;
184
200
Ok ( handlebars)
185
201
}
186
202
@@ -194,28 +210,35 @@ async fn main() -> anyhow::Result<()> {
194
210
let _ = std:: fs:: remove_dir_all ( & output_dir) ;
195
211
std:: fs:: create_dir_all ( & output_dir) ?;
196
212
197
- let assets = compile_assets ( Path :: new ( "." ) , & output_dir, "/" ) ?;
213
+ let root_dir = Path :: new ( "." ) ;
214
+ let assets = compile_assets ( root_dir, & output_dir, "/" ) ?;
198
215
let handlebars = setup_handlebars ( ) ?;
199
216
200
217
let ctx = RenderCtx {
218
+ template_dir : root_dir. join ( "templates" ) ,
201
219
fluent_loader : create_loader ( ) ,
202
220
assets,
203
221
rust_version,
204
222
teams,
205
223
handlebars,
206
224
output_dir,
207
225
} ;
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
+ ) ?;
209
231
210
232
render_index ( & ctx) ?;
211
233
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" ) ?;
217
240
218
- // TODO: 404, redirects
241
+ // TODO: redirects
219
242
220
243
Ok ( ( ) )
221
244
}
@@ -243,18 +266,78 @@ fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
243
266
render_ctx
244
267
. page ( "governance/index" , "governance-page-title" , & data, lang)
245
268
. 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 ( ( ) )
247
293
}
248
294
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 ( ( ) )
260
343
}
0 commit comments