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 ;
911use clap:: Parser ;
1012use figment:: Figment ;
1113use mas_config:: {
@@ -27,14 +29,19 @@ pub(super) struct Options {
2729#[ derive( Parser , Debug ) ]
2830enum 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
3340impl 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