Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

variant of html-to-string-macro which returns something that impl ToString (return the nodes themselves) #25

Open
vidhanio opened this issue Jul 26, 2023 · 15 comments

Comments

@vidhanio
Copy link

vidhanio commented Jul 26, 2023

typed_html does this, and i believe it allows them to do cool things like having iterators in the macro. currently, i believe you would have to use the html! macro to create strings in the loop then join them at the end, which seems kinda jank.

would this be possible?

edit: written by myself: https://github.com/vidhanio/html-node

@vidhanio vidhanio changed the title variant html-to-string-macro which returns something that impl ToString (return the nodes themselves) variant of html-to-string-macro which returns something that impl ToString (return the nodes themselves) Jul 26, 2023
@infogulch
Copy link

I opened and discussed a similar idea in the original fork of this project with the author:

stoically#37

Quoting:

I'm thinking about a new implementation of the html! macro, maybe called html-to-reader-macro, that instead of emitting a String it emits a value that implements the Read trait. The idea being that you can build up a big graph of objects that impl Read and then calling read() on the root will traverse through the component tree and scan out all the bytes directly into the final destination buffer exactly once with no unnecessary intermediate copying.

I agree it would probably be a big performance benefit, though I suppose we should measure it before making definitive claims. (To be fair, it shouldn't be hard to show that reducing copying and temporary allocations improves performance.)

@vidhanio
Copy link
Author

vidhanio commented Jul 26, 2023

i've made an implementation. shall i push to a fork and let you take a look?

also, i didn't really think about the zero-copy. i wanted to make mine as easy-to-use as possible. However, it does use Display so it can be written to any formatter.

@vidhanio
Copy link
Author

vidhanio commented Jul 26, 2023

alright @infogulch, i've got this test case passing! i'll push my code and update you when it's ready to take a look at.

use html_node::{html, text};

#[test]
fn works() {
    let html = html!(
        <div>
            <ul>
                { (0..5).map(|i| html!(<li>{ text!("Item {i}") }</li>)) }
            </ul>
        </div>
    );

    let expected = concat!(
        "<div>",
        "<ul>",
        "<li>Item 0</li>",
        "<li>Item 1</li>",
        "<li>Item 2</li>",
        "<li>Item 3</li>",
        "<li>Item 4</li>",
        "</ul>",
        "</div>",
    );

    assert_eq!(html.to_string(), expected);
}

@vidhanio
Copy link
Author

pinging @vldm as well ^

@vidhanio
Copy link
Author

@infogulch
Copy link

Oh so it works by implementing the format trait which makes sense. So would you be able to use something like write!(&mut out, "{html:#}")?

@vidhanio
Copy link
Author

vidhanio commented Jul 27, 2023

@infogulch - yep! the # flag enables pretty-printing, whereas without it, it all prints on one line (much like how Debug with #? works)

@vldm
Copy link
Collaborator

vldm commented Jul 27, 2023

@vidhanio
Hi, awesome work!
I thought of adding more complex example of rstml usage (maybe even to fork typed-html). But haven't a lot of spare time to do it.
Originally i have created rs-tml organisation to keep tools/examples in one place, and to be able to manage ownership of rstml repository.

So if you want, we can move html-node to rs-tml organisation.
Or/and we can add link to it in README section of rstml.

I would like to keep original html-to-string macro in examples and not replace it, since it is a minimal usage variant and doesn't involve any additional runtime.

@vidhanio
Copy link
Author

vidhanio commented Jul 27, 2023

@vldm - maybe there could be a section called "powered by rstml" in the readme that this could be put under? then maybe you could create an example which uses html-node in this repository as well.

@vidhanio
Copy link
Author

vidhanio commented Jul 28, 2023

@vldm @infogulch i think y'all would be interested in taking a look at this, i added a super cool new feature!

https://docs.rs/html-node/latest/html_node/typed/index.html

@infogulch
Copy link

Interesting, I like how you can define custom elements and attributes. In my case I used syn-rsx html_to_string_macro with htmx which adds several attributes starting with hx-* to basically all elements. I attempted to use this 'stack' in the wasm-service project, but I was only moderately happy with it. Do you think the typed feature covers this use case?

@vidhanio
Copy link
Author

vidhanio commented Jul 28, 2023

@infogulch what a coincidence, i originally had the idea to create this to make HTMX in rust much easier and well, wasm-service was the original project that got me interested in HTMX!! 🤯 small world!

the cool thing about the typed feature i added is that it supports any tag you want, if you add a data- prefix to it. does htmx support data-* prefixes for their hx-* tags?

@infogulch
Copy link

I'm delighted to have played a small part in your inspiration to pursue this project!

As far as I know htmx only works with the hx- prefix, but I may be mistaken.

@vidhanio
Copy link
Author

i'm away from the keyboard rn, but i have an idea for how to support hx-* prefix attributes. i'll have an update out when i get home and code it up.

@vidhanio
Copy link
Author

vidhanio commented Jul 29, 2023

@infogulch... a few hours later, i've cooked up something AMAZING

#[derive(Debug, Clone)]
struct Location {
    x: i32,
    y: i32,
}

impl Display for Location {
    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
        write!(f, "{},{}", self.x, self.y)
    }
}

typed::element! {
    MyElement("my-element") {
        location: Location,
    }
}

typed::attributes! {
    [Hx] {
        hx_boost: bool,
    }
}
            // vvvvvvvvvvvvvvvvvvvvvvvvvvvvv attribute extension list
typed::html! { (hx: Hx, extras)
    ...
}

image
image
image

ident: Type in the "extensions" list will use the Type as an extension set of typed attributes, and those with just ident will be added to the list of "other" attributes like data-* and aria-*

and you can have as many extensions as you want!
image

and then, you can create your own decl macro for your crate that wraps typed::html! to add your custom attribute extensions for all macros.

macro_rules! html_hx {
    ($($tt:tt)*) => {::html_node::typed::html!((hx: $crate::Hx) $($tt)*)};
}

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants