nate/
lib.rs

1// Copyright (c) 2021-2022 René Kijewski <crates.io@k6i.de>
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14//
15// --- LLVM Exceptions to the Apache 2.0 License ----
16//
17// As an exception, if, as a result of your compiling your source code, portions
18// of this Software are embedded into an Object form of such source code, you
19// may redistribute such embedded portions in such Object form without complying
20// with the conditions of Sections 4(a), 4(b) and 4(d) of the License.
21//
22// In addition, if you combine or link compiled forms of this Software with
23// software that is licensed under the GPLv2 ("Combined Software") and if a
24// court of competent jurisdiction determines that the patent provision (Section
25// 3), the indemnity provision (Section 9) or other Section of the License
26// conflicts with the conditions of the GPLv2, you may retroactively and
27// prospectively choose to deem waived or otherwise exclude such Section(s) of
28// the License, but only in their entirety and only with respect to the Combined
29// Software.
30
31#![cfg_attr(docsrs, feature(doc_cfg))]
32#![forbid(unsafe_code)]
33#![allow(unused_attributes)]
34#![warn(absolute_paths_not_starting_with_crate)]
35#![warn(elided_lifetimes_in_paths)]
36#![warn(explicit_outlives_requirements)]
37#![warn(meta_variable_misuse)]
38#![warn(missing_copy_implementations)]
39#![warn(missing_debug_implementations)]
40#![warn(missing_docs)]
41#![warn(non_ascii_idents)]
42#![warn(noop_method_call)]
43#![warn(single_use_lifetimes)]
44#![warn(trivial_casts)]
45#![warn(unreachable_pub)]
46#![warn(unused_crate_dependencies)]
47#![warn(unused_extern_crates)]
48#![warn(unused_lifetimes)]
49#![warn(unused_results)]
50
51//! ## NaTE — Not a Template Engine
52//!
53//! [![GitHub Workflow Status](https://img.shields.io/github/actions/workflow/status/Kijewski/nate/ci.yml?branch=main)](https://github.com/Kijewski/nate/actions/workflows/ci.yml)
54//! [![Crates.io](https://img.shields.io/crates/v/nate?logo=rust)](https://crates.io/crates/nate)
55//! ![Minimum supported Rust version](https://img.shields.io/badge/rustc-1.56+-important?logo=rust "Minimum Supported Rust Version")
56//! [![License](https://img.shields.io/badge/license-Apache--2.0%20WITH%20LLVM--exception-informational?logo=apache)](https://github.com/Kijewski/nate/blob/v0.2.2/LICENSE "Apache-2.0 WITH LLVM-exception")
57//!
58//! This is *not* a template engine, but sugar to implicitly call `write!(…)` like in PHP.
59//! The only difference is that the output gets XML escaped automatically unless opted-out explicitly.
60//!
61//! Unlike other template engines like
62//! [Askama](https://crates.io/crates/askama), [Handlebars](https://crates.io/crates/handlebars),
63//! [Liquid](https://github.com/cobalt-org/liquid-rust), [Tera](https://crates.io/crates/tera), or
64//! [Tide](https://crates.io/crates/tide), you don't have to learn a new language.
65//! If you know Rust and HTML, you already know how to implement templates with NaTE!
66//!
67//! E.g.
68//!
69//! *   templates/greeting.html:
70//!
71//!     ```xml
72//!     <h1>Hello, {{user}}!</h1>
73//!     ```
74//!
75//!     The path is relative to the cargo manifest dir (where you find Cargo.toml) of the project.
76//!
77//! *   src/main.rs:
78//!
79//!     ```ignore
80//!     use std::fmt::Write;
81//!     use nate::Nate;
82//!     
83//!     #[derive(Nate)]
84//!     #[template(path = "templates/greeting.html")]
85//!     struct Greetings<'a> {
86//!         user: &'a str,
87//!     }
88//!     
89//!     let mut output = String::new();
90//!     let tmpl = Greetings { user: "<World>" };
91//!     write!(output, "{}", tmpl).unwrap();
92//!     println!("{}", output);
93//!     ```
94//!
95//! *   Output:
96//!
97//!     ```html
98//!     <h1>Hello, &#60;World&#62;!</h1>
99//!     ```
100//!
101//! No new traits are needed, instead `#[derive(Nate)]` primarily works by implementing [`fmt::Display`](core::fmt::Display).
102//! This also makes nesting of NaTE templates possible.
103//!
104//! A more complex example would be:
105//!
106//! *   src/main.rs:
107//!
108//!     ```ignore
109//!     use std::fmt::Write;
110//!     use nate::Nate;
111//!
112//!     #[derive(Nate)]
113//!     #[template(path = "templates/99-bottles.html")]
114//!     struct Template {
115//!         limit: usize,
116//!     }
117//!
118//!     #[test]
119//!     fn ninetynine_bottles_of_beer() {
120//!         print!("{}", Template { limit: 99 });
121//!     }
122//!     ```
123//!
124//! *   templates/99-bottles.txt:
125//!
126//!     ```jinja
127//!     {%-
128//!         for i in (1..=self.limit).rev() {
129//!             if i == 1 {
130//!     -%}
131//!     1 bottle of beer on the wall.
132//!     1 bottle of beer.
133//!     Take one down, pass it around.
134//!     {%-
135//!             } else {
136//!     -%}
137//!     {{i}} bottles of beer on the wall.
138//!     {{i}} bottles of beer.
139//!     Take one down, pass it around.
140//!
141//!     {%
142//!             }
143//!         }
144//!     -%}
145//!     ```
146//!
147//! Inside of a `{% code block %}` you can write any and all rust code.
148//!
149//! Values in `{{ value blocks }}` are printed XML escaped.
150//!
151//! Values in `{{{ raw blocks }}}` are printed verbatim.
152//!
153//! For values in `{{{{ debug blocks }}}}` their debug message is printed as in `"{:?}"`.
154//!
155//! For values in `{{{{{ verbose blocks }}}}}` their debug message is printed verbose as in `"{:#?}"`.
156//!
157//! With `{< include >}` blocks you can include a template file.
158//! It then behaves like it was copy-pasted into the current file.
159//! If the path starts with "." or "..", the file is searched relative to the current file.
160//! Otherwise it is search in the project root.
161//!
162//! Using hyphens `-` at the start/end of a block, whitespaces before/after the block are trimmed.
163//!
164//! Data blocks `{{…}}` to `{{{{{…}}}}}` and includes `{<…>}` must not be empty.
165//! Code `{%…%}` and comment `{#…#}` blocks may be empty.
166//!
167//! Blocks don't need to be closed at the end of the file.
168//!
169//! To debug any errors you can add an argument as in `#[template(generated = "some/path/generated.rs")]`.
170//! The generated code is stored in there even if there were parsing errors in the Rust code.
171//! The path is relative to the project root (where your Cargo.toml lives).
172//!
173//! ## Feature flags
174//!
175//! * `std` <sup>\[enabled by default\]</sup> — enable features found in [`std`] crate, e.g. printing the value of a [`MutexGuard`](std::sync::MutexGuard)
176//!
177//! * `alloc` <sup>\[enabled by default, enabled by `std`\]</sup> — enable features found in the [`alloc`] crate, e.g. [`io::Write`](std::io::Write)
178//!
179
180#[cfg(doc)]
181extern crate alloc;
182#[cfg(doc)]
183extern crate std;
184
185#[doc(hidden)]
186pub mod details;
187mod escape;
188mod fast_float;
189mod fast_integer;
190mod raw;
191
192pub use ::nate_derive::{addr, Nate};
193
194pub use crate::details::{EscapeWrapper, RenderInto, WriteAny};
195pub use crate::fast_float::FloatMarker;
196pub use crate::fast_integer::IntMarker;
197pub use crate::raw::RawMarker;