display-as 0.3.0

Compile-time templates for displaying data in different markup formats.
docs.rs failed to build display-as-0.3.0
Please check build logs and if you believe this is docs.rs' fault, report into this issue report.

This template crate uses and defines a [DisplayAs] trait, which allows a type to be displayed in a particular format.

Overview

This crate defines three things that you need be aware of in order to use it: the [Format] trait, which defines a markup language or other format, the [DisplayAs] trait which is implemented for any type that can be converted into some [Format], and finally the template language and macros which allow you to conveniently implement [DisplayAs] for your own types. I will describe each of these concepts in order. (FIXME I should also have a quick-start...)

[Format]

There are a number of predefined Formats (and I can easily add more if there are user requests), so the focus here will be on using these Formats, rather than on defining your own (which also isn't too hard). A format is a zero-size type that has a rule for escaping strings and an associated MIME type. The builtin formats include [HTML], [LaTeX], and [Math] (which is math-mode LaTeX).

[DisplayAs]<F>

The [DisplayAs]<F: Format> trait is entirely analogous to the Display trait in the standard library, except that it is parametrized by a [Format] so you can have different representations for the same type in different formats. This also makes it harder to accidentally include the wrong representation in your output.

Most of the primitive types already have [DisplayAs] implemented for the included Formats. If you encounter a type that you wish had [DisplayAs] implemented for a given format, just let me know. You can manually implement [DisplayAs] for any of your own types (it's not worse than implementing Display) but that isn't how you are intended to do things (except perhaps in very simple cases, like a wrapper around an integer). Instead you will want to use a template to implement [DisplayAs] for your own types.

Templates!

There are two template macros that you can use. If you just want to get a string out of one or more [DisplayAs] objects, you will use something like format_as!(HTML, "hello world" value). If you want to implement [DisplayAs], you will use the attribute [with_template!]. In these examples I will use [format_as!] because that makes it easy to write testable documentation. But in practice you will most likely primarily use the [with_template] attribute.

String literals

The first thing you can include in a template is a string literal, which is treated literally.

use display_as::{HTML, format_as};
assert_eq!(&format_as!(HTML, "Treat this literally <" ),
           "Treat this literally <");

Expressions

String literals are essential to representing some other [Format]. To include your data in the output, you can include any expression that yields a type with [DisplayAs]<F> where F is your [Format]. Each expression is delimited by string literals (or the other options below). Note that since an expression is

use display_as::{HTML, format_as};
let s = "This is not a literal: <";
assert_eq!(&format_as!(HTML, s ),
           "This is not a literal: &lt;");

Blocks and conditionals

You can use braces to enclose any template expression. Any rust code before the braces is treated as literal rust. This enables you to write conditionals, match expressions, and loops.

use display_as::{HTML, format_as};
assert_eq!(&format_as!(HTML,
                       for i in 1..4 {
                           "Counting " i "...\n"
                       }
                       "Blast off!"),
           "Counting 1...\nCounting 2...\nCounting 3...\nBlast off!");

Semicolons

You may also play any rust statements you wish, if you end them with a semicolon. This enables you to define local variables.

use display_as::{HTML, format_as};
assert_eq!(&format_as!(HTML, "I am counting " let count = 5;
                             count " and again " count ),
           "I am counting 5 and again 5");

Embedding a different format

You can also embed in one format a representation from another type. This can be helpful, for instance, if you want to use MathJax to handle LaTeX math embedded in an HTML file.

use display_as::{HTML, Math, format_as};
assert_eq!(&format_as!(HTML, "The number $" 1.2e12 as Math "$"),
           r"The number $1.2\times10^{12}$");

Saving a portion of a template for reuse

You can also save a template expression using a let statement, provided the template expression is enclosed in braces. This allows you to achieve goals similar to the base templates in Jinja2. (Once we have an include feature... Example to come in the future.)

use display_as::{HTML, format_as};
assert_eq!(&format_as!(HTML,
                       let x = 1;
                       let announce = { "number " x };
                       "The " announce " is silly " announce),
           "The number 1 is silly number 1");

Differences when putting a template in a file

You will most likely always put largish templates in a separate file. This makes editing your template simpler and keeps things in general easier. The template language for templates held in a distinct file has one differnce from those shown above: the file always begins and ends with string literals, but their initial and final quotes respectively are omitted. Furthermore, the first and last string literals must be "raw" literals with a number of # signs equal to the maximum used in the template. I suggest using an equal number of # signs for all string literals in a given template. Thus a template might look like:

<html>
  <body>
    "## self.title r##"
  </body>
</html>

You can see that the quotes appear "inside out." This is intentional, so that for most formats the quotes will appear to enclose the rust code rather than everything else, and as a result editors will hopefully be able to do the "right thing" for the template format (e.g. HTML in this case).

Using include!("...") within a template

Now I will demonstrate how you can include template files within other template files by using the include! macro within a template. To demonstrate this, we will need a few template files.

We will begin with a "base" template that describes how a page is laid out.

base.html:

We can have a template for how we will display students...

student.html:

``
Finally, an actual web page describing a class!
#### `class.html`:
```ignore

Now to put all this together, we'll need some rust code.

use display_as::{DisplayAs, HTML, format_as, with_template};
struct Student { name: &'static str };
#[with_template("student.html")]
impl DisplayAs<HTML> for Student {}

struct Class { coursename: &'static str, coursenumber: usize, students: Vec<Student> };
#[with_template("class.html")]
impl DisplayAs<HTML> for Class {}

let myclass = Class {
      coursename: "Templates",
      coursenumber: 365,
      students: vec![Student {name: "David"}, Student {name: "Joel"}],
};
assert_eq!(&format_as!(HTML, myclass), r#"<title>PH365: Templates</title>
<html>
  <ul>

  // This is buggy:  I want to iterate, but it fails!
  for s in self.students.iter() {
    "<li>" s "</li>"
  }

  </ul>
</html>


#);