Skip to content

Commit ab52f7e

Browse files
authored
templates check command: Allow saving rendered samples (#5202)
2 parents d2309bf + f633e42 commit ab52f7e

File tree

5 files changed

+448
-243
lines changed

5 files changed

+448
-243
lines changed

crates/cli/src/commands/templates.rs

Lines changed: 52 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,10 @@
44
// SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial
55
// Please see LICENSE files in the repository root for full details.
66

7-
use std::process::ExitCode;
7+
use std::{fmt::Write, process::ExitCode};
88

9+
use anyhow::{Context as _, bail};
10+
use camino::Utf8PathBuf;
911
use clap::Parser;
1012
use figment::Figment;
1113
use mas_config::{
@@ -27,14 +29,19 @@ pub(super) struct Options {
2729
#[derive(Parser, Debug)]
2830
enum Subcommand {
2931
/// Check that the templates specified in the config are valid
30-
Check,
32+
Check {
33+
/// If set, templates will be rendered to this directory.
34+
/// The directory must either not exist or be empty.
35+
#[arg(long = "out-dir")]
36+
out_dir: Option<Utf8PathBuf>,
37+
},
3138
}
3239

3340
impl Options {
3441
pub async fn run(self, figment: &Figment) -> anyhow::Result<ExitCode> {
3542
use Subcommand as SC;
3643
match self.subcommand {
37-
SC::Check => {
44+
SC::Check { out_dir } => {
3845
let _span = info_span!("cli.templates.check").entered();
3946

4047
let template_config = TemplatesConfig::extract_or_default(figment)
@@ -68,12 +75,51 @@ impl Options {
6875
let templates = templates_from_config(
6976
&template_config,
7077
&site_config,
71-
&url_builder,
72-
// Use strict mode in template checks
78+
&url_builder, // Use strict mode in template checks
7379
true,
7480
)
7581
.await?;
76-
templates.check_render(clock.now(), &mut rng)?;
82+
let all_renders = templates.check_render(clock.now(), &mut rng)?;
83+
84+
if let Some(out_dir) = out_dir {
85+
// Save renders to disk.
86+
if out_dir.exists() {
87+
let mut read_dir =
88+
tokio::fs::read_dir(&out_dir).await.with_context(|| {
89+
format!("could not read {out_dir} to check it's empty")
90+
})?;
91+
if read_dir.next_entry().await?.is_some() {
92+
bail!("Render directory {out_dir} is not empty, refusing to write.");
93+
}
94+
} else {
95+
tokio::fs::create_dir(&out_dir)
96+
.await
97+
.with_context(|| format!("could not create {out_dir}"))?;
98+
}
99+
100+
for ((template, sample_identifier), template_render) in &all_renders {
101+
let (template_filename_base, template_ext) =
102+
template.rsplit_once('.').unwrap_or((template, "txt"));
103+
let template_filename_base = template_filename_base.replace('/', "_");
104+
105+
// Make a string like `-index=0-browser-session=0-locale=fr`
106+
let sample_suffix = {
107+
let mut s = String::new();
108+
for (k, v) in &sample_identifier.components {
109+
write!(s, "-{k}={v}")?;
110+
}
111+
s
112+
};
113+
114+
let render_path = out_dir.join(format!(
115+
"{template_filename_base}{sample_suffix}.{template_ext}"
116+
));
117+
118+
tokio::fs::write(&render_path, template_render.as_bytes())
119+
.await
120+
.with_context(|| format!("could not write render to {render_path}"))?;
121+
}
122+
}
77123

78124
Ok(ExitCode::SUCCESS)
79125
}

0 commit comments

Comments
 (0)