Skip to content

Commit c3c85fc

Browse files
committed
Restructure code around and add redirect template
1 parent c370145 commit c3c85fc

File tree

5 files changed

+357
-311
lines changed

5 files changed

+357
-311
lines changed

src/fs.rs

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,3 +9,17 @@ pub fn ensure_directory(path: &Path) -> anyhow::Result<()> {
99
}
1010
Ok(())
1111
}
12+
13+
pub fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
14+
std::fs::create_dir_all(&dst)?;
15+
for entry in std::fs::read_dir(src)? {
16+
let entry = entry?;
17+
let ty = entry.file_type()?;
18+
if ty.is_dir() {
19+
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
20+
} else {
21+
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
22+
}
23+
}
24+
Ok(())
25+
}

src/main.rs

Lines changed: 10 additions & 260 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,19 @@
1-
use crate::assets::{AssetFiles, compile_assets};
2-
use crate::fs::ensure_directory;
3-
use crate::i18n::{EXPLICIT_LOCALE_INFO, LocaleInfo, SUPPORTED_LOCALES, TeamHelper, create_loader};
4-
use crate::rust_version::{RustVersion, fetch_rust_version};
5-
use crate::teams::{PageData, RustTeams, encode_zulip_stream, load_rust_teams};
1+
use crate::assets::compile_assets;
2+
use crate::i18n::{TeamHelper, create_loader};
3+
use crate::redirect::create_redirects;
4+
use crate::render::{RenderCtx, render_directory, render_governance, render_index};
5+
use crate::rust_version::RustVersion;
6+
use crate::teams::{encode_zulip_stream, load_rust_teams};
67
use anyhow::Context;
78
use handlebars::{DirectorySourceOptions, Handlebars};
8-
use handlebars_fluent::{FluentHelper, Loader, SimpleLoader};
9-
use serde::Serialize;
10-
use std::ffi::OsStr;
11-
use std::fs::File;
12-
use std::io::BufWriter;
9+
use handlebars_fluent::FluentHelper;
1310
use std::path::{Path, PathBuf};
1411

1512
mod assets;
1613
mod fs;
1714
mod i18n;
15+
mod redirect;
16+
mod render;
1817
mod rust_version;
1918
mod teams;
2019

@@ -33,155 +32,6 @@ fn baseurl(lang: &str) -> String {
3332
}
3433
}
3534

36-
#[derive(Serialize)]
37-
struct TemplateCtx<'a, T: Serialize> {
38-
page: String,
39-
title: String,
40-
parent: &'static str,
41-
is_landing: bool,
42-
data: &'a T,
43-
lang: String,
44-
baseurl: String,
45-
pontoon_enabled: bool,
46-
assets: &'a AssetFiles,
47-
locales: &'static [LocaleInfo],
48-
is_translation: bool,
49-
}
50-
51-
struct PageCtx<'a, T: Serialize> {
52-
template_ctx: TemplateCtx<'a, T>,
53-
output_dir: &'a Path,
54-
handlebars: &'a Handlebars<'a>,
55-
}
56-
57-
impl<'a, T: Serialize> PageCtx<'a, T> {
58-
fn make_landing(mut self) -> Self {
59-
self.template_ctx.is_landing = true;
60-
self
61-
}
62-
63-
fn render<P: AsRef<Path>>(self, dst_path: P) -> anyhow::Result<()> {
64-
let path = dst_path.as_ref();
65-
let template = &self.template_ctx.page;
66-
67-
let out_path = self.output_dir.join(path);
68-
ensure_directory(&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-
);
73-
eprintln!("Rendering `{template}` into {}", out_path.display());
74-
75-
self.handlebars
76-
.render_to_write(template, &self.template_ctx, &mut output_file)
77-
.with_context(|| {
78-
anyhow::anyhow!(
79-
"cannot render template {template} into {}",
80-
out_path.display()
81-
)
82-
})?;
83-
Ok(())
84-
}
85-
}
86-
87-
struct RenderCtx<'a> {
88-
handlebars: Handlebars<'a>,
89-
template_dir: PathBuf,
90-
fluent_loader: SimpleLoader,
91-
output_dir: PathBuf,
92-
rust_version: RustVersion,
93-
teams: RustTeams,
94-
assets: AssetFiles,
95-
}
96-
97-
impl<'a> RenderCtx<'a> {
98-
fn page<T: Serialize>(
99-
&'a self,
100-
page: &str,
101-
title_id: &str,
102-
data: &'a T,
103-
lang: &str,
104-
) -> PageCtx<'a, T> {
105-
let title = if title_id.is_empty() {
106-
"".into()
107-
} else {
108-
let lang = lang.parse().expect("lang should be valid");
109-
self.fluent_loader.lookup(&lang, title_id, None)
110-
};
111-
PageCtx {
112-
template_ctx: TemplateCtx {
113-
page: page.to_string(),
114-
title,
115-
parent: LAYOUT,
116-
is_landing: false,
117-
data,
118-
baseurl: baseurl(&lang),
119-
is_translation: lang != "en-US",
120-
lang: lang.to_string(),
121-
pontoon_enabled: PONTOON_ENABLED,
122-
assets: &self.assets,
123-
locales: EXPLICIT_LOCALE_INFO,
124-
},
125-
output_dir: &self.output_dir,
126-
handlebars: &self.handlebars,
127-
}
128-
}
129-
130-
fn copy_asset_dir<P: AsRef<Path>>(&self, src_dir: P, dst_dir: P) -> anyhow::Result<()> {
131-
let dst = self.output_dir.join(dst_dir.as_ref());
132-
println!(
133-
"Copying static asset directory from {} to {}",
134-
src_dir.as_ref().display(),
135-
dst.display()
136-
);
137-
copy_dir_all(src_dir.as_ref(), dst)?;
138-
Ok(())
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-
}
152-
}
153-
154-
/// Calls `func` for all supported languages.
155-
/// Passes it the destination path into which should a given page be rendered, and the language
156-
/// in which it should be rendered.
157-
fn all_langs<F>(dst_path: &str, func: F) -> anyhow::Result<()>
158-
where
159-
F: Fn(&str, &str) -> anyhow::Result<()>,
160-
{
161-
for lang in SUPPORTED_LOCALES.iter() {
162-
let path = match lang {
163-
l if *l == ENGLISH => dst_path.to_string(),
164-
l => format!("{l}/{dst_path}"),
165-
};
166-
func(&path, lang).with_context(|| anyhow::anyhow!("could not handle language {lang}"))?;
167-
}
168-
Ok(())
169-
}
170-
171-
fn copy_dir_all(src: impl AsRef<Path>, dst: impl AsRef<Path>) -> std::io::Result<()> {
172-
std::fs::create_dir_all(&dst)?;
173-
for entry in std::fs::read_dir(src)? {
174-
let entry = entry?;
175-
let ty = entry.file_type()?;
176-
if ty.is_dir() {
177-
copy_dir_all(entry.path(), dst.as_ref().join(entry.file_name()))?;
178-
} else {
179-
std::fs::copy(entry.path(), dst.as_ref().join(entry.file_name()))?;
180-
}
181-
}
182-
Ok(())
183-
}
184-
18535
fn setup_handlebars() -> anyhow::Result<Handlebars<'static>> {
18636
let mut handlebars: Handlebars<'static> = Handlebars::new();
18737
handlebars.set_strict_mode(true);
@@ -237,107 +87,7 @@ async fn main() -> anyhow::Result<()> {
23787
render_directory(&ctx, "tools")?;
23888
render_directory(&ctx, "what")?;
23989
ctx.page("404", "", &(), ENGLISH).render("404.html")?;
90+
create_redirects(&ctx)?;
24091

241-
// TODO: redirects
242-
243-
Ok(())
244-
}
245-
246-
fn render_index(render_ctx: &RenderCtx) -> anyhow::Result<()> {
247-
#[derive(Serialize)]
248-
struct IndexData {
249-
rust_version: String,
250-
}
251-
let data = IndexData {
252-
rust_version: render_ctx.rust_version.0.clone(),
253-
};
254-
all_langs("index.html", |path, lang| {
255-
render_ctx
256-
.page("index", "", &data, lang)
257-
.make_landing()
258-
.render(path)
259-
})
260-
}
261-
262-
fn render_governance(render_ctx: &RenderCtx) -> anyhow::Result<()> {
263-
let data = render_ctx.teams.index_data();
264-
265-
all_langs("governance/index.html", |dst_path, lang| {
266-
render_ctx
267-
.page("governance/index", "governance-page-title", &data, lang)
268-
.render(dst_path)
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(())
293-
}
294-
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-
}
34292
Ok(())
34393
}

0 commit comments

Comments
 (0)