Giter VIP home page Giter VIP logo

horrorshow-rs's Introduction

Horrorshow

Documentation crates.io

A macro-based html templating library, compatible with stable rust (currently requires rust >= 1.48).

Features

This crate will degrade gracefully when compiled without std (disable the "std" feature) and even without alloc (disable the "alloc" feature).

When compiled with alloc but without std:

  • Template::write_to_io() is not defined.
  • Templates may only emit errors implementing ToString and all such errors are immediately converted to strings.

When compiled with just core:

  • RenderBox is no longer defined (no allocation).
  • The Template::into_string() and Template::write_to_string() are no longer defined. The only template rendering method available is Template::write_to_fmt().
  • Templates may only emit static &str errors, and only the first is recorded.

Example:

#[macro_use]
extern crate horrorshow;
use horrorshow::prelude::*;
use horrorshow::helper::doctype;

fn main() {
    let actual = format!("{}", html! {
        : doctype::HTML;
        html {
            head {
                title : "Hello world!";
            }
            body {
                // attributes
                h1(id="heading") {
                    // Insert escaped text
                    : "Hello! This is <html />"
                }
                p {
                    // Insert raw text (unescaped)
                    : Raw("Let's <i>count</i> to 10!")
                }
                ol(id="count") {
                    // You can embed for loops, while loops, and if statements.
                    @ for i in 0..10 {
                        li(first? = (i == 0)) {
                            // Format some text.
                            : format_args!("{}", i+1)
                        }
                    }
                }
                // You need semi-colons for tags without children.
                br; br;
                p {
                    // You can also embed closures.
                    |tmpl| {
                        tmpl << "Easy!";
                    }
                }
            }
        }
    });

    let expected = "\
    <!DOCTYPE html>\
    <html>\
      <head>\
        <title>Hello world!</title>\
      </head>\
      <body>\
        <h1 id=\"heading\">Hello! This is &lt;html /&gt;</h1>\
        <p>Let's <i>count</i> to 10!</p>\
        <ol id=\"count\">\
          <li first>1</li>\
          <li>2</li>\
          <li>3</li>\
          <li>4</li>\
          <li>5</li>\
          <li>6</li>\
          <li>7</li>\
          <li>8</li>\
          <li>9</li>\
          <li>10</li>\
        </ol>\
        <br><br>\
        <p>Easy!</p>\
      </body>\
    </html>";
    assert_eq!(expected, actual);
}

horrorshow-rs's People

Contributors

akubera avatar dashed avatar dependabot[bot] avatar djc avatar fenhl avatar jfransham avatar lambda-fairy avatar niederb avatar oli-obk avatar stebalien avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar

horrorshow-rs's Issues

HTML boolean attribute support?

I'm unclear on whether or not horrorshow supports boolean attributes, like required:

<input type="text" value="value" required />

I tried assigning it a boolean value but bool doesn't implement RenderOnce so that didn't work.

Thanks!

Infinite recursion with RenderOnce::size_hint

Excerpt from lldb output

[...]
Process 20325 stopped
* thread #7, stop reason = EXC_BAD_ACCESS (code=2, address=0x700009117ff8)
    frame #0: 0x000000010007eeb0 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
   102 	
   103 	    #[inline]
   104 	    fn size_hint(&self) -> usize {
-> 105 	        RenderBox::size_hint_box(self)
   106 	    }
   107 	}
   108
Target 0: (priroda) stopped.
(lldb) bt
[...]
* thread #7, stop reason = EXC_BAD_ACCESS (code=2, address=0x700009117ff8)
  * frame #0: 0x000000010007eeb0 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #1: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #2: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #3: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #4: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #5: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #6: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #7: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #8: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #9: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #10: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #11: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #12: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #13: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #14: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #15: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #16: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #17: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
[...]
    frame #64895: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #64896: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #64897: 0x0000000100074065 priroda`_$LT$T$u20$as$u20$horrorshow..render..RenderBox$GT$::size_hint_box::h782068eaecdd52ae(self=0x0000700009313078) at render.rs:79
    frame #64898: 0x000000010007eeb5 priroda`_$LT$alloc..boxed..Box$LT$horrorshow..render..RenderBox$u20$$u2b$$u20$core..marker..Send$u20$$u2b$$u20$$u27$b$GT$$u20$as$u20$horrorshow..render..RenderOnce$GT$::size_hint::hcc77a206d30b40bb(self=0x0000700009313078) at render.rs:105
    frame #64899: 0x0000000100073565 priroda`horrorshow::template::Template::into_string::h347df756764525af(self=Box<RenderBox> @ 0x0000700009313078) at template.rs:13
[...]

Use write_char

use write_char when writing to a fmt::Write. This is only stable in 1.2.

README Example Panics

When pasting the README example, I get an assertion panic. You can see what that looks like below:

~/D/test_horrorshow (master|✔) $ cargo run
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
     Running `target/debug/test_horrorshow`
thread 'main' panicked at 'assertion failed: `(left == right)`
  left: `"<!DOCTYPE html><html><head><title>Hello world!</title></head><body><h1 id=\"heading\">Hello! This is &lt;html /&gt;</h1><p>Let's <i>count</i> to 10!</p><ol id=\"count\"><li first>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ol><br /><br /><p>Easy!</p></body></html>"`,
 right: `"<!DOCTYPE html><html><head><title>Hello world!</title></head><body><h1 id=\"heading\">Hello! This is &lt;html /&gt;</h1><p>Let's <i>count</i> to 10!</p><ol id=\"count\"><li first>1</li><li>2</li><li>3</li><li>4</li><li>5</li><li>6</li><li>7</li><li>8</li><li>9</li><li>10</li></ol><br><br><p>Easy!</p></body></html>"`', src/main.rs:69:5
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
~/D/test_horrorshow (master|✔) [101]$ cargo check
    Finished dev [unoptimized + debuginfo] target(s) in 0.00s
~/D/test_horrorshow (master|✔) $ 

As you can see, the difference comes in the <br> tags, where the assertion assumes that the break will read <br /> instead, like in XHTML. I think it would look nice to fix this to avoid confusion.

Anyways, here's the full example for easy pasting:

README example:

#[macro_use]
extern crate horrorshow;
use horrorshow::prelude::*;
use horrorshow::helper::doctype;

fn main() {
    let actual = format!("{}", html! {
        : doctype::HTML;
        html {
            head {
                title : "Hello world!";
            }
            body {
                // attributes
                h1(id="heading") {
                    // Insert escaped text
                    : "Hello! This is <html />"
                }
                p {
                    // Insert raw text (unescaped)
                    : Raw("Let's <i>count</i> to 10!")
                }
                ol(id="count") {
                    // You can embed for loops, while loops, and if statements.
                    @ for i in 0..10 {
                        li(first? = (i == 0)) {
                            // Format some text.
                            : format_args!("{}", i+1)
                        }
                    }
                }
                // You need semi-colons for tags without children.
                br; br;
                p {
                    // You can also embed closures.
                    |tmpl| {
                        tmpl << "Easy!";
                    }
                }
            }
        }
    });

    let expected = "\
    <!DOCTYPE html>\
    <html>\
      <head>\
        <title>Hello world!</title>\
      </head>\
      <body>\
        <h1 id=\"heading\">Hello! This is &lt;html /&gt;</h1>\
        <p>Let's <i>count</i> to 10!</p>\
        <ol id=\"count\">\
          <li first>1</li>\
          <li>2</li>\
          <li>3</li>\
          <li>4</li>\
          <li>5</li>\
          <li>6</li>\
          <li>7</li>\
          <li>8</li>\
          <li>9</li>\
          <li>10</li>\
        </ol>\
        <br /><br />\
        <p>Easy!</p>\
      </body>\
    </html>";
    assert_eq!(expected, actual);
}

How to make a layout that accepts content?

Say I wanted to make a layout that I can inject different content into based on a route. How could I go about doing so?

fn layout(page_title: String, content: ???) -> String {
  format!("{}",html!{
    head {
      title : page_title;
    }
    body {
      :content
    }
  })
}

fn home_content() -> Box<RenderOnce> {

  box_html! {
    h1 { :"Home Page" }
  }
}

fn about_content() -> Box<RenderOnce> {

  box_html! {
    h1 { :"About Us" }
  }
}

fn contact_content() -> Box<RenderOnce> {

  box_html! {
    h1 { :"Contact Us" }
  }
}

//and i could call the layout like this

layout(String::from("Home"),home_content());
layout(String::from("About"),about_content());
layout(String::from("Contact"),contact_content());

Feature request: render to `Bytes`

It's common in Rust web server frameworks to manage instances of bytes::Bytes, which is essentially a reference counted Vec. It's sufficiently ubiquitous that I think there'd be value in adding write_to_bytes or into_bytes to Template (perhaps behind a feature flag).

wishlist: Explore applicability to DOM creation

The data structure created by horrorshow might easily be suitable to create a DOM from it as well, in parallel to serialization to text-encoded HTML. Some features of horrorshow might be unsupported (I'm unsure as to how much raw text can be used with modern DOM APIs), but that might be acceptable for use cases.

The typed-html crate provides something some steps in that direction (I haven't seen an example of actual shared HTML, but it can be used to render either to HTML or to dodrio), but is not actively maintained any more, and horrorshow's syntax seems nicer to me.

The application I see for it would be to use the same code to render things server side, but (with a vastly different backend) to also perform client-side updates from WASM.

There was one previous issue in that direction (#29) where DOM was described as out-of-scope, so this is more about exploring DOM creation based on horrorshow, for which I'd like to gather input here:

  • Has creating a DOM from RenderOnce objects been attempted?
  • Are there any major known showstoppers?
  • Would the project be open to (possibly under a feature flag) exposing internals that would allow other crates to implement DOM rendering based on horrorshow's templates? (I'm far from understanding its internals yet, but that could for example be a trait around TemplateBuffer that can be implemented by a DOM builder.)

Implement BoxedRender*

BoxedRenderMut, BoxedRenderOnce, BoxRender. We need to be able to return owned templates. Having three types is annoying but flexible.

Empty div not closing properly

The code that is causing this error is below:

fn make_div() -> Box<dyn RenderBox> {
    box_html! {
        div(id="renders-incorrectly");
    }
}

The final div with id renders-incorrectly doesn't render <div id="renders-incorrectly"></div>. Instead, it renders <div id="renders-incorrectly"> and then appends a closing </div> at the end of the entire document. Replacing that div with:

:Raw("<div id=\"renders-correctly\"></div>");

fixes the problem, although I think they should be the same thing.

I'd like to look into this problem and try to fix it myself when I get the time.

Nice error messages

Since rust 1.20.0, we have a compile_error! macro. We should use this to make error messages not suck.

Possibility for custom tags with hyphenated names?

Loving this library so far!

It's fairly common to use custom HTML tags, eg. <alert type="success">..</alert> instead of <span class="alert alert-success">..</span> and obviously this example works with horrorshow.

Custom tags also sometimes include hyphens, though, particularly if the tags are being namespaced (eg. angular and <ng-*> tags). Horrorshow supports tag names with underscores, but is there any way to allow hyphens as well?

My Rust macro game is bad - I assume this is a no-go because hyphen isn't valid in an identifier.

Utility for conditionally joining classNames together

I had a need for something like https://github.com/JedWatson/classnames

I came up with the following macro:

macro_rules! classnames {

    // base cases

    ($name: expr) => (
        format!("{}", $name)
    );

    ($name:expr => $should_include:expr) => (
        if $should_include {
            format!("{}", $name)
        } else {
            format!("")
        }
    );

    // expansions

    ($name:expr, $($tail:tt)+) => {
        format!("{} {}", $name, classnames!($($tail)*))

    };

    ($name:expr => $should_include:expr, $($tail:tt)+) => {
        if $should_include {
            format!("{} {}", $name, classnames!($($tail)*))
        } else {
            classnames!($($tail)*)
        };
    };

}

// usage:
html!{

    div(class = classnames!("active" => true, "button-style")) {
        : "this is a button"
    }

    div(class = classnames!("active" => false, "button-style")) {
        : "this is a button"
    }
}

rust playground with examples: https://is.gd/e6Jahb

I just learned rust macros to make this; it can probably be refactored into something more nicer.


I'm wondering if this utility macro would be something worth adding to horrorshow. I can try to PR.

Context-aware escaping

This is necessary to be able to safely e.g., have colors for elements that are determined at runtime. It is also necessary to support proper URL handling.

Missing Fragment Specifier Warning

Got the following warning when compiling horrorshow 0.5.7 with rust 1.17.0:

warning: missing fragment specifier
   --> <append_html macros>:127:7
    |
127 | } $ ( $ next ) * ) => {
    |       ^^^^^^
    |
    = note: #[warn(missing_fragment_specifier)] on by default
    = warning: this was previously accepted by the compiler but is being phased out; it will become a hard error in a future release!
    = note: for more information, see issue #40107 <https://github.com/rust-lang/rust/issues/40107>

Combining templates with `fold`/`match` -> no two closures have the same type.

I want to fold some Horrorshow templates, I'm using Horrorshow to make directory listings. However, I'm having a hard time understanding how to make this work, because I can't match on the output of html! and I can't fold over them either.

I'm sorry for the glorified support request, I'll try to rework your advice into a pull-request which adds examples to the documentation if you like.

Here's the code I want to write, but the only way to make it work I have found is passing around the rendered HTML by calling .into_string().unwrap() on the html! output every time.

(Sorry for the large blurb of code, the first map is largely irrelevant but I thought it would be useful for context.)

  fn list_directories_recursive(&self, path: &Path) -> Option<String> {
        let listings = path
        .read_dir().unwrap()
        .filter_map(|de| de.ok())         // remove invalid paths
        .map(|de| de.path())              // DirectoryEntry -> Path
        .filter(|p| self.visible_path(p)) // remove invisible paths
        .map(|path| {
            if let &Some(href) = &path.as_os_str().to_str() {
                if let &Some(file_name) = &path.file_name() {
                    if let Some(name) = file_name.to_str() {
                        let href = href.replacen(".", "", 1); // ./thing -> /thing
                        let name = name.replacen("/", "", 1); // ./dir/thing -> thing

                        return Some((path, href, name));
                    }
                }
            }

            None
        })
        .filter_map(|s| s)               // remove empty/non-UTF directories
        .map(|(path, href, name)| {      // Path -> String
            html! {
                @if self.list_recursively && path.is_dir() {
                    @if let Some(sub) = self.list_directories_recursive(&path) {
                        : sub
                    }
                }
                tr {
                    td {
                        a(href=&href) {
                            : name
                        }
                    }
                }
            }
        })
        .fold(html! {}, |acc, html| html! {
            : acc;
            : html;
        })
        .into_string().unwrap();

        if listings.len() != 0 {
            Some(listings)
        } else {
            None
        }

Doctype?

Hi there,

And thanks for Horrorshow!

Just a dumb question: how to insert the document type (<!doctype html>)?

Callbacks/passing values from the DOM back into Rust?

I'm trying to implement a "callback" where as a user types, the content they have typed thus far is sent back to Rust for some processing (namely, autocomplete). I'm unclear on the syntax or in general how this would be accomplished given the tools exposed in horrorshow. So, my question is two parts:
a) Is this possible at all?
b) If yes to (a), what would that look like?

After I figure this out, I would like to write up examples/documentation and contribute them to the repository to help others out.

Thanks!

improving example

thanks for your library! Unfortunately I am struggling to find out how to pass a simple variable to the template e.g.

let actual = html! {
    : doctype::HTML;
    html {
        head {
            title : "Hello world!";
        }
    }
}

how would I pass the variable title using your example ? I also think web templates are mostly used to display data so perhaps you can add that to the example..not sure why the calculations are important in this scenario! sorry if I misunderstood something...
am I also right to assume that normal HTML has to be translated into the rust tags inside html! to work right ?

Pretty output?

For deeply nested horrorshow templates, it may be useful to have a pretty html output (i.e. space/tabs). Would this be something that may be added to horrorshow-rs?

For now I'm relying on chrome devtools.

error: no rules expected the token `tmpl`

As a prenote I only have a general idea of how rust works and are making a web site/server to learn it.

I have multiple functions that return Box<RenderBox> that are later used in a function that returns the rendered string.

Version: 0.6.2

Log

error: no rules expected the token `tmpl`
   --> src\resources\partials\element.rs:131:5
    |
131 | /     box_html! {
132 | |         nav(class="nav nav_top full_width") {
133 | |             ul(class="nav__menu flex flex--nowrap flex__center wrapper") {
134 | |                 li(class="nav__items--container") {
...   |
151 | |         }
152 | |     }
    | |_____^
    |
    = note: this error originates in a macro outside of the current crate

Code

    box_html! {
        nav(class="nav nav_top full_width") {
            ul(class="nav__menu flex flex--nowrap flex__center wrapper") {
                li(class="nav__items--container") {
                    ul(class="nav__items--list flex flex--nowrap") {
                        li(class="flex__item") {
                            a(href="/", class="nav__link") {
                                : "Story";
                                b : "Archive";
                            }
                        }
                        : navbar_item(item_grow)
                        @ if navbar_drawer {
                            : navbar_drawer(drawer);
                        } else {
                            : navbar_item(item_login);
                        }
                    }
                }
            }
        }
    }

Indenting/reformatting in IntelliJ doesn't fix indentation

If I reformat the document in Intellij, it doesn't fix the indentation within the macro, so if I start with:

        pub(crate) mod table {
        use horrorshow::prelude::*;
        use horrorshow::helper::doctype;

        pub(crate) fn render() -> Box<horrorshow::RenderBox> {
            box_html! {
        table {
            thead {
             tr {
                th {: "Column 1"}
                th {: "Column 1"}
                th {: "Column 1"}
                }
            }
                        tbody {
                        tr {
                td {: "Data 1"}
                td {: "Data 1"}
                td {: "Data 1"}
                }
            }
            }

           label(typ="text", name="name");
           : "First criteria"
    }
        }
    }

and then ask IntelliJ for reformat, it doesn't change it at all. I am also not getting any errors inline until I manually ask it to compile/build.

Is this expected and simply a price of using Rust's macros? or have I missed something?

(it's a price well paid as the compile time safety is great!)

no std support? + specify Cargo.toml in README

Problem One: I need to know if this crate can be used in a project that is run with #![no_std].
Problem Two: You should probably put how to add the crate to your Cargo.toml in the README. I had to dig through the horrorshow.rs Cargo.toml file to find the version.

Just some suggestions (and a question). 😺

Implement template into ActixWeb

I love the templating system. I am also fairly new to Rust. I come from a Python/Django background and have decided to switch for various reasons. I can't figure out how to get ActixWeb to render the template, it just gives me the escaped string. Below is what I tried, but feel free to use any of the routes available and I'll figure it out (eventually).

#[macro_use]
extern crate horrorshow;
use horrorshow::prelude::*;
use horrorshow::helper::doctype;
use actix_web::{web, App, HttpResponse, HttpServer, Responder};

fn template() -> String {
    let my_title = "Hello world!";
    let actual = format!("{}", html! {
        : doctype::HTML;
        html {
            head {
                title:my_title;
            }
            body {
                h1(id="heading", class="title") : my_title;
                p {
                    : "Hello! This is <html>";
                }
            }
        }
    });
    return actual;
}

async fn index() -> impl Responder {
    template()
}

#[actix_rt::main]
async fn main() -> std::io::Result<()> {

    HttpServer::new(|| {
        App::new()
            .service(
                web::scope("/app").route("/index.html", web::get().to(index)),
            )
    })
    .bind("127.0.0.1:8000")?
    .run()
    .await
}

Indenting / nesting content macro

Hi, I think I have a (partial) solution to the nesting problem: a function to format content as-if it is a nested and indented HTML element.

For instance, if I generate some HTML like so:

let content = html! {
    ul {
        li { : "toot" }
    }
}

I get the following HTML:

<ul><li>toot</li></ul>

As an aside, this puzzles me, is there anything I can do to get indented output? It'd be a great feature to me, I would expect output like this

<ul>
    <li>
        toot
    </li>
</ul>

Anyway, now I want to insert it into an HTML page:

let page = html! {
    html {
        body {
            : Raw(content)
        }
    }
}

And this gets me:

<html><body><ul><li>toot</li></ul></body></html>

Now, what I'm suggesting is a function or character to insert raw text with newlines and an indent, like so:

let page = html! {
    html {
        body {
            > content
        }
    }
}

Or perhaps: > Raw(content), or : Block(content), or some other variant.

For the result:

<html><body>
    <ul><li>toot</li></ul>
</body></html>

It would take the indent level from the parent's indent level and add one to it. If the raw content is multiline, it would indent all lines separately, like so:

<html><body>
    <ul><li>toot</li></ul>
    <ul><li>toot</li></ul>
</body></html>

What do you think? Would this be hard to implement? It'd really be a boon to getting nice formatted HTML output out of my Markdown server project (https://gitlab.com/willemmali-rust/markbrowse).

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.