1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210
//! Templates in your Rust code. //! //! This library allows you to write HTML templates, using macros, mixed in //! with your Rust code. This allows you to use normal Rust code for the logic, //! and embed template code along side it. //! //! **Contents:** //! //! 1. [Basics](#basics) //! 1. [Rendering](#rendering) //! 1. [Escaping](#escaping) //! 1. [Returning Errors](#returning-errors) //! 1. [Whitespace](#whitespace) //! //! # Basics //! //! The most basic template is this: //! //! ``` //! use qtpl::{tplfn, tpl}; //! //! #[tplfn] //! fn hello(name: &str) { //! tpl! {Hello, <strong>{name}</strong>!} //! } //! ``` //! //! There are a few things going on here -- first, we're adding the `#[tlpfn]` //! attribute to our template function. This makes it possible to use this //! function as a template. Second, we're using the `tpl!` macro inside the body //! and embedding some textual content. Lastly, we're putting the variable //! `name` inside another block. //! //! # Rendering //! //! Fundamentally rendering happens to something that implements //! `std::io::Write`. This means you could potentially write directly to a //! socket. Usually you'll buffer the content entirely, or use a buffered //! writer at the least. //! //! ## To a `File` //! ``` //! # use qtpl::{tplfn, tpl}; //! # //! # #[tplfn] //! # fn hello(name: &str) { //! # tpl! {Hello, <strong>{name}</strong>!} //! # } //! # //! let mut file = std::fs::File::create("/tmp/qtpl.txt")?; //! hello(&mut file, "world")?; //! # //! # Ok::<(), std::io::Error>(()) //! ``` //! //! ## To a `Vec<u8>` //! ``` //! # use qtpl::{tplfn, tpl}; //! # //! # #[tplfn] //! # fn hello(name: &str) { //! # tpl! {Hello, <strong>{name}</strong>!} //! # } //! # //! let mut out = vec![]; //! hello(&mut out, "world")?; //! assert_eq!(out, b"Hello, <strong>world</strong>!"); //! # //! # Ok::<(), std::io::Error>(()) //! ``` //! //! ## To a `String` for Testing //! Purely as a convinience, a `render_string!` macro is provided which //! panics on errors, and returns a `String`. Remember, this is useful for //! testing and documentation, but you shouldn't be using this in production //! code, because it involves unnecessary copies and conversions. //! //! ``` //! # use qtpl::{tplfn, tpl, render_string}; //! # //! # #[tplfn] //! # fn hello(name: &str) { //! # tpl! {Hello, <strong>{name}</strong>!} //! # } //! # //! assert_eq!(render_string!(hello("world")), "Hello, <strong>world</strong>!"); //! # //! # Ok::<(), std::io::Error>(()) //! ``` //! //! # Escaping //! //! The default escaping used by the library is geared towards HTML. Using the //! same example above: //! //! ``` //! # use qtpl::{tplfn, tpl, render_string}; //! # //! # #[tplfn] //! # fn hello(name: &str) { //! # tpl! {Hello, <strong>{name}</strong>!} //! # } //! # //! assert_eq!(render_string!(hello("<world>")), "Hello, <strong><world></strong>!"); //! # //! # Ok::<(), std::io::Error>(()) //! ``` //! //! # Returning Errors //! //! The `#[tplfn]` attribute will add a return type of `std::io::Result<()>`, //! but only if one isn't present. You can customize the return type and take //! control of the errors being returned. The only requirement is that //! `std::io::Error` types can be converted into that error using the usual //! process. //! //! A contrived but real example: //! //! ``` //! # use qtpl::{tplfn, tpl, render_string}; //! # //! type BoxError = Box<dyn std::error::Error + Send + Sync>; //! //! #[tplfn] //! fn answer(a: &str) -> Result<(), BoxError> { //! let a: i8 = a.parse()?; //! tpl! {{&a.to_string()}} //! } //! //! assert_eq!(render_string!(answer("42")), "42"); //! //! let mut w = vec![]; //! match answer(&mut w, "not a number") { //! Result::Err(err) => { //! assert_eq!(format!("{}", err), "invalid digit found in string"); //! }, //! _ => panic!("expected an error"), //! }; //! ``` //! //! # Whitespace //! //! The library takes an opinionated stance on whitespace. The rules are as //! follows: //! //! * Whitespace at the begining of the template is stripped. //! * Whitespace at the end of the template is stripped. //! * Whitespace around a whitelisted set of elements, where it should be //! insignificant is stripped. //! * All whitespace, including newlines is collapsed into a single space. //! * Rules only apply to template text, contents of varibles are not modified. //! //! This example shows all the rules in action, including how certain tags //! behave differently, and how multiple spaces including newlines are //! collapsed: //! //! ``` //! # use qtpl::{tplfn, tpl, render_string}; //! # //! #[tplfn] //! fn home() { //! tpl! { //! <div> //! <a>Go <i class="icon"> </i></a> //! <a>{" "}</a> //! </div> //! } //! } //! //! assert_eq!( //! render_string!(home()), //! concat!( //! "<div>", //! r#"<a>Go <i class="icon"> </i></a>"#, //! " ", //! "<a> </a>", //! "</div>", //! ), //! ); //! ``` //! //! Note how the space inside and around the `<i>` tag is preserved, but the //! space around the `<div>` tag is stripped. Also notice how the multiple //! spaces inside the `<i>` are collapsed into a single space. #![doc(html_favicon_url = "https://raw.githubusercontent.com/daaku/qtpl/master/assets/favicon.png")] #![doc(html_logo_url = "https://raw.githubusercontent.com/daaku/qtpl/master/assets/logo.png")] pub use qtpl_macros::{child, render_string, tpl, tplfn}; use std::io::{Result, Write}; // This is used internally for escaping in macro output. #[doc(hidden)] pub use v_htmlescape::escape; // This is used internally for closures created by child components and is // automatically implemented by them. #[doc(hidden)] pub trait Render { fn render(self, destination: &mut dyn Write) -> Result<()>; } impl<F> Render for F where F: FnOnce(&mut dyn Write) -> Result<()>, { fn render(self, destination: &mut dyn Write) -> Result<()> { self(destination) } }