Skip to content

Commit d72e08a

Browse files
authored
Block building after all rendering finished (#201)
1 parent 5e8334d commit d72e08a

File tree

6 files changed

+73
-68
lines changed

6 files changed

+73
-68
lines changed

src/build.rs

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -21,12 +21,6 @@ pub async fn watch_build<P: AsRef<Path>>(
2121
data::load(&source);
2222

2323
let source_path = source.clone();
24-
tokio::spawn(async move {
25-
tokio::signal::ctrl_c().await.unwrap();
26-
// Save zine data only when the process gonna exist
27-
data::export(source_path).unwrap();
28-
std::process::exit(0);
29-
});
3024

3125
let mut engine = ZineEngine::new(source, dest, zine)?;
3226
// Spawn the build process as a blocking task, avoid starving other tasks.
@@ -39,6 +33,13 @@ pub async fn watch_build<P: AsRef<Path>>(
3933
}
4034

4135
if watch {
36+
tokio::spawn(async move {
37+
tokio::signal::ctrl_c().await.unwrap();
38+
// Save zine data only when the process gonna exist
39+
data::export(source_path).unwrap();
40+
std::process::exit(0);
41+
});
42+
4243
println!("Watching...");
4344
let (tx, rx) = mpsc::channel();
4445
let mut debouncer = new_debouncer(Duration::from_millis(500), None, tx)?;
@@ -80,6 +81,8 @@ pub async fn watch_build<P: AsRef<Path>>(
8081
Err(err) => println!("watch error: {:?}", &err),
8182
}
8283
}
84+
} else {
85+
data::export(source_path).unwrap();
8386
}
8487
anyhow::Ok(())
8588
})

src/code_blocks/mod.rs

Lines changed: 33 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
use std::collections::HashMap;
22

33
use anyhow::{bail, Result};
4-
use tokio::{runtime::Handle, task};
54

65
mod author;
76
mod callout;
@@ -46,48 +45,44 @@ impl<'a> Fenced<'a> {
4645
/// otherwise return URL preview error HTML string to remind user we have error.
4746
///
4847
/// If the fenced is unsupported, we simply return `None`.
49-
pub fn render_code_block(self, block: &'a str) -> Option<String> {
48+
#[tokio::main(flavor = "current_thread")]
49+
pub async fn render_code_block(self, block: &'a str) -> Option<String> {
5050
match self.name {
5151
URL_PREVIEW => {
5252
let url = block.trim();
5353

54-
// Block in place to execute async task
55-
task::block_in_place(|| {
56-
Handle::current().block_on(async {
57-
let (first_preview, mut rx) = {
58-
// parking_lot RwLock guard isn't async-aware,
59-
// we should keep this guard drop in this scope.
60-
let data = data::read();
61-
if let Some(info) = data.get_preview(url) {
62-
let html = UrlPreviewBlock::new(self.options, url, info)
63-
.render()
64-
.unwrap();
65-
return Some(html);
66-
}
67-
68-
data.preview_url(url)
69-
};
70-
rx.changed()
71-
.await
72-
.expect("URL preview watch channel receive failed.");
73-
let event = rx.borrow();
74-
match event.to_owned().expect("Url preview didn't initialized.") {
75-
PreviewEvent::Finished(info) => {
76-
let html = UrlPreviewBlock::new(self.options, url, info)
77-
.render()
78-
.unwrap();
79-
if first_preview {
80-
println!("URL previewed: {url}");
81-
}
82-
Some(html)
83-
}
84-
PreviewEvent::Failed(err) => {
85-
// Return a preview error block.
86-
Some(UrlPreviewError(url, &err).render().unwrap())
87-
}
54+
let (first_preview, mut rx) = {
55+
// parking_lot RwLock guard isn't async-aware,
56+
// we should keep this guard drop in this scope.
57+
let data = data::read();
58+
if let Some(info) = data.get_preview(url) {
59+
let html = UrlPreviewBlock::new(self.options, url, info)
60+
.render()
61+
.unwrap();
62+
return Some(html);
63+
}
64+
65+
data.preview_url(url)
66+
};
67+
rx.changed()
68+
.await
69+
.expect("URL preview watch channel receive failed.");
70+
let event = rx.borrow();
71+
match event.to_owned().expect("Url preview didn't initialized.") {
72+
PreviewEvent::Finished(info) => {
73+
let html = UrlPreviewBlock::new(self.options, url, info)
74+
.render()
75+
.unwrap();
76+
if first_preview {
77+
println!("URL previewed: {url}");
8878
}
89-
})
90-
})
79+
Some(html)
80+
}
81+
PreviewEvent::Failed(err) => {
82+
// Return a preview error block.
83+
Some(UrlPreviewError(url, &err).render().unwrap())
84+
}
85+
}
9186
}
9287
CALLOUT => {
9388
let html = CalloutBlock::new(self.options, block).render().unwrap();

src/entity/article.rs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use std::{borrow::Cow, collections::HashMap, fs, path::Path};
22

33
use anyhow::{ensure, Context as _, Result};
4+
use rayon::prelude::{ParallelBridge, ParallelIterator};
45
use serde::{Deserialize, Serialize};
56
use tera::Context;
67
use time::Date;
@@ -260,9 +261,10 @@ impl Entity for Article {
260261
fn render(&self, mut context: Context, dest: &Path) -> Result<()> {
261262
context.insert("i18n", &self.get_translations());
262263
Article::render(self, context.clone(), dest)?;
263-
for article in self.i18n.values() {
264-
Article::render(article, context.clone(), dest)?;
265-
}
264+
265+
self.i18n.values().par_bridge().for_each(|article| {
266+
Article::render(article, context.clone(), dest).expect("Failed to render article");
267+
});
266268

267269
Ok(())
268270
}

src/entity/issue.rs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,10 @@
11
use std::{borrow::Cow, fs, path::Path};
22

33
use anyhow::{Context as _, Result};
4-
use rayon::slice::ParallelSliceMut;
4+
use rayon::{
5+
prelude::{IndexedParallelIterator, IntoParallelRefIterator, ParallelIterator},
6+
slice::ParallelSliceMut,
7+
};
58
use serde::{Deserialize, Serialize};
69
use tera::Context;
710
use time::Date;
@@ -158,19 +161,20 @@ impl Entity for Issue {
158161
.filter(|article| article.need_publish())
159162
.collect::<Vec<_>>();
160163
// Render articles with number context.
161-
for (index, article) in articles.iter().enumerate() {
162-
let mut context = context.clone();
163-
context.insert("siblings", &self.sibling_articles(index));
164-
context.insert("number", &(index + 1));
165-
166-
let dest = issue_dir.clone();
167-
let article = (*article).clone();
168-
tokio::task::spawn_blocking(move || {
164+
articles
165+
.par_iter()
166+
.enumerate()
167+
.for_each(|(index, article)| {
168+
let mut context = context.clone();
169+
context.insert("siblings", &self.sibling_articles(index));
170+
context.insert("number", &(index + 1));
171+
172+
let dest = issue_dir.clone();
173+
let article = (*article).clone();
169174
article
170175
.render(context, &dest)
171176
.expect("Render article failed.");
172177
});
173-
}
174178

175179
context.insert("articles", &articles);
176180
context.insert(

src/entity/mod.rs

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
use anyhow::Result;
2-
use rayon::iter::{IntoParallelRefMutIterator, ParallelIterator};
2+
use rayon::{
3+
iter::{IntoParallelRefMutIterator, ParallelIterator},
4+
prelude::IntoParallelRefIterator,
5+
};
36
use std::path::Path;
47
use tera::Context;
58

@@ -66,14 +69,11 @@ impl<T: Entity + Sync + Send + Clone + 'static> Entity for Vec<T> {
6669
}
6770

6871
fn render(&self, context: Context, dest: &Path) -> Result<()> {
69-
for entity in self {
72+
self.par_iter().try_for_each(|entity| {
7073
let entity = entity.clone();
7174
let context = context.clone();
7275
let dest = dest.to_path_buf();
73-
tokio::task::spawn_blocking(move || {
74-
entity.render(context, &dest).expect("Render failed.")
75-
});
76-
}
77-
Ok(())
76+
entity.render(context, &dest)
77+
})
7878
}
7979
}

src/entity/zine.rs

Lines changed: 5 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,7 @@
11
use anyhow::{Context as _, Result};
22
use rayon::{
33
iter::{IntoParallelRefIterator, ParallelBridge, ParallelExtend, ParallelIterator},
4+
prelude::IntoParallelRefMutIterator,
45
slice::ParallelSliceMut,
56
};
67
use serde::{Deserialize, Serialize};
@@ -311,7 +312,7 @@ impl Entity for Zine {
311312
fn parse(&mut self, source: &Path) -> Result<()> {
312313
self.theme.parse(source)?;
313314

314-
self.topics.iter_mut().try_for_each(|(id, topic)| {
315+
self.topics.par_iter_mut().try_for_each(|(id, topic)| {
315316
topic.id = id.clone();
316317
topic.parse(source)
317318
})?;
@@ -334,7 +335,7 @@ impl Entity for Zine {
334335
if self.authors.is_empty() {
335336
println!("Warning: no author specified in [authors] of root `zine.toml`.");
336337
} else {
337-
self.authors.iter_mut().try_for_each(|(id, author)| {
338+
self.authors.par_iter_mut().try_for_each(|(id, author)| {
338339
author.id = id.clone();
339340
// Fallback to default zine avatar if neccessary.
340341
if author.avatar.is_none()
@@ -439,13 +440,13 @@ impl Entity for Zine {
439440
// Render home page.
440441
let issues = self
441442
.issues
442-
.iter()
443+
.par_iter()
443444
.filter(|issue| issue.need_publish())
444445
.collect::<Vec<_>>();
445446
context.insert("issues", &issues);
446447
// `article_map` is the issue number and issue's featured articles map.
447448
let article_map = issues
448-
.iter()
449+
.par_iter()
449450
.map(|issue| (issue.number, issue.featured_articles()))
450451
.collect::<HashMap<u32, Vec<_>>>();
451452
context.insert("article_map", &article_map);

0 commit comments

Comments
 (0)