1+ use anyhow:: Result ;
12use serde:: Deserialize ;
23use std:: cell:: RefCell ;
3- use std:: collections:: { BTreeMap , HashMap } ;
4+ use std:: collections:: BTreeMap ;
45use std:: path:: { Path , PathBuf } ;
5-
66use tera:: Tera ;
77
8- use crate :: { RendererError , Result } ;
9-
10- use super :: RenderingContext ;
11-
12- fn make_strip_prefix_function ( ) -> impl tera:: Function {
13- move |args : & HashMap < String , serde_json:: value:: Value > | -> tera:: Result < tera:: Value > {
14- let string = args
15- . get ( "s" )
16- . ok_or_else ( || tera:: Error :: from ( format ! ( "No s argument provided" ) ) ) ?
17- . as_str ( )
18- . ok_or_else ( || tera:: Error :: from ( format ! ( "S has invalid type, expected string" ) ) ) ?;
19- let prefix = args
20- . get ( "prefix" )
21- . ok_or_else ( || tera:: Error :: from ( format ! ( "No prefix argument provided" ) ) ) ?
22- . as_str ( )
23- . ok_or_else ( || {
24- tera:: Error :: from ( format ! ( "Prefix has invalid type, expected string" ) )
25- } ) ?;
26- string
27- . strip_prefix ( prefix)
28- . map ( |s| tera:: Value :: String ( s. to_owned ( ) ) )
29- . ok_or_else ( || tera:: Error :: from ( format ! ( "Could not strip prefix" ) ) )
30- }
31- }
32-
338pub struct CustomComponent {
349 template : Tera ,
3510 name : String ,
@@ -38,14 +13,7 @@ pub struct CustomComponent {
3813}
3914
4015impl CustomComponent {
41- pub fn new ( name : & str , template_str : & str , dependencies : & [ & Self ] ) -> Result < CustomComponent > {
42- let mut template = Tera :: default ( ) ;
43- for dep in dependencies {
44- template. extend ( & dep. template ) ?;
45- }
46- template. add_raw_template ( name, template_str) ?;
47- template. register_function ( "strip_prefix" , make_strip_prefix_function ( ) ) ;
48-
16+ pub fn new ( name : & str , template : Tera ) -> Result < CustomComponent > {
4917 Ok ( CustomComponent {
5018 name : String :: from ( name) ,
5119 counter : RefCell :: new ( 0 ) ,
@@ -57,33 +25,17 @@ impl CustomComponent {
5725 self . template . register_function ( name, function) ;
5826 }
5927
60- fn create_context (
61- & self ,
62- rendering_context : & RenderingContext ,
63- attributes : BTreeMap < String , String > ,
64- ) -> tera:: Context {
65- let counter = self . counter . replace_with ( |& mut counter| counter + 1 ) ;
66- let mut context = tera:: Context :: new ( ) ;
67- context. insert ( "counter" , & counter) ;
68- context. insert ( "language" , & rendering_context. language ) ;
69- context. insert ( "path" , & rendering_context. path ) ;
70- context. insert ( "ctx" , & rendering_context. serialized_ctx ) ;
71- context. insert (
72- "book_dir" ,
73- & rendering_context. ctx . destination . parent ( ) . unwrap ( ) ,
74- ) ;
75- context. insert ( "attributes" , & attributes) ;
76-
77- context
78- }
79-
8028 pub fn render (
8129 & self ,
82- rendering_context : & RenderingContext ,
30+ tera_context : & tera :: Context ,
8331 attributes : BTreeMap < String , String > ,
8432 ) -> Result < String > {
85- let context = self . create_context ( rendering_context, attributes) ;
86- let output = self . template . render ( & self . name , & context) ?;
33+ let counter = self . counter . replace_with ( |& mut counter| counter + 1 ) ;
34+ let mut tera_context = tera_context. clone ( ) ;
35+ tera_context. insert ( "count" , & counter) ;
36+ tera_context. insert ( "attributes" , & attributes) ;
37+
38+ let output = self . template . render ( & self . name , & tera_context) ?;
8739 Ok ( output)
8840 }
8941
@@ -93,41 +45,69 @@ impl CustomComponent {
9345}
9446
9547#[ derive( Deserialize ) ]
96- pub struct TeraComponentConfig {
97- pub name : String ,
98- pub path : PathBuf ,
99-
100- #[ serde( default ) ]
101- pub dependencies : Vec < String > ,
48+ pub enum Component {
49+ Named { name : String , path : PathBuf } ,
50+ Anonymous ( PathBuf ) ,
10251}
10352
53+ /// Configuration in `book.toml` `[output.tera-renderer]`.
10454#[ derive( Deserialize ) ]
10555pub struct TeraRendererConfig {
106- pub components : Vec < TeraComponentConfig > ,
56+ /// Relative path to the templates directory.
57+ pub templates_dir : PathBuf ,
58+ /// Custom HTML components to register.
59+ #[ serde( default ) ]
60+ pub html_components : Vec < Component > ,
10761}
10862
10963impl TeraRendererConfig {
110- pub fn create_components ( & self , current_dir : & Path ) -> Result < Vec < CustomComponent > > {
111- let mut name_to_component = HashMap :: new ( ) ;
112- for component in & self . components {
113- let component_path = current_dir. join ( & component. path ) ;
114- let template_str = std:: fs:: read_to_string ( & component_path) ?;
115- let dependencies = component
116- . dependencies
117- . iter ( )
118- . map ( |name| {
119- name_to_component. get ( name) . ok_or_else ( || {
120- RendererError :: DependencyNotFound ( format ! (
121- "Could not find depdendency {}" ,
122- name
123- ) )
124- } )
125- } )
126- . collect :: < Result < Vec < _ > > > ( ) ?;
127- let new_component =
128- CustomComponent :: new ( & component. name , & template_str, & dependencies) ?;
129- name_to_component. insert ( component. name . clone ( ) , new_component) ;
64+ fn add_templates_recursively ( tera_template : & mut Tera , directory : & Path ) -> Result < ( ) > {
65+ for entry in std:: fs:: read_dir ( directory) ? {
66+ let entry = entry?;
67+ let path = entry. path ( ) ;
68+ if path. is_dir ( ) {
69+ Self :: add_templates_recursively ( tera_template, & path) ?;
70+ } else {
71+ tera_template. add_template_file ( & path, path. file_name ( ) . unwrap ( ) . to_str ( ) ) ?;
72+ }
13073 }
131- Ok ( name_to_component. into_values ( ) . collect ( ) )
74+ Ok ( ( ) )
75+ }
76+
77+ fn create_custom_components ( & self , tera_template : & Tera ) -> Result < Vec < CustomComponent > > {
78+ self . html_components
79+ . iter ( )
80+ . map ( |component| {
81+ Ok ( match component {
82+ Component :: Named { name, path } => {
83+ let mut template = tera_template. clone ( ) ;
84+ template. add_template_file ( path, Some ( name) ) ?;
85+ CustomComponent :: new ( name, template) ?
86+ }
87+ Component :: Anonymous ( path) => {
88+ let name = path
89+ . file_name ( )
90+ . unwrap_or_default ( )
91+ . to_str ( )
92+ . unwrap_or_default ( ) ;
93+ CustomComponent :: new ( name, tera_template. clone ( ) ) ?
94+ }
95+ } )
96+ } )
97+ . collect ( )
98+ }
99+
100+ pub fn create_template_and_components (
101+ & self ,
102+ current_dir : & Path ,
103+ ) -> Result < ( Tera , Vec < CustomComponent > ) > {
104+ let mut tera_template = Tera :: default ( ) ;
105+ Self :: add_templates_recursively (
106+ & mut tera_template,
107+ & current_dir. join ( & self . templates_dir ) ,
108+ ) ?;
109+ let components = self . create_custom_components ( & tera_template) ?;
110+
111+ Ok ( ( tera_template, components) )
132112 }
133113}
0 commit comments