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

Access maud's AST and change (e.g. add attribute) programatically? #365

Open
yatesco opened this issue Jan 14, 2023 · 4 comments
Open

Access maud's AST and change (e.g. add attribute) programatically? #365

yatesco opened this issue Jan 14, 2023 · 4 comments
Labels

Comments

@yatesco
Copy link

yatesco commented Jan 14, 2023

Hi there,

I am writing the same fragment of HTML over and over and want to extract a helper function to do it. In this case the helper function will do something like:

form {
    ....
(passed_in_element)
}

and is called like:

let contents = html! {
  div {....}
};
let form = my_form_helper::render(contents);

I really want the helper function to add an attribute to the passed_in_element - is this possible?

Thanks!

@lambda-fairy
Copy link
Owner

Hi @yatesco!

Unfortunately I don't know of a clean way to do that. The html! macro compiles down to string concatenation. None of the AST exists at runtime.

There are also security implications to passing arbitrary attributes around. In the future, I'd like to implement context-aware escaping (#181). But for that to work, each attribute must understand the context in which its used. An example is the src attribute – a user-supplied src on an <img> is (mostly) harmless, but that same src on a <script> can lead to XSS.

What's the attribute that you want to add to the element? Can you capture all the possibilities in an enum?

@yatesco
Copy link
Author

yatesco commented Jan 28, 2023

hi @lambda-fairy - so the idea is that the app is primarily HTML served from the back, which is of course why I'm using this excellent tool :-), but I want to enhance the front end with snippets of dynamic behaviour, similar to htmx.org.

I have a series of attributes which are processed at runtime to introduce dynamic behaviour, so, for example, I might have a (badly designed) accordion that looks like:

<div my-attribute-type="accordion">
  <div my-acccordion-index="1"></div>
  <div my-acccordion-index="2"></div>
  <div my-acccordion-index="3"></div>
  <div my-acccordion-index="4"></div>
</div>

It's simple to have an fn:

fn accordion(pages: Vec<AccordionPage>) -> Markup {

}

But what I really want is:

fn accordion(pages: Vec<Markup>) -> Markup {}

which I call like:

accordion(vec![html! { "first page" }, html! {"second page"}])

and that's where I'm blocked. I can use an HTML parser lib to do this, but I wondered if maud had something OOTB.

@rben01
Copy link

rben01 commented Mar 20, 2023

On the one hand, I agree that having the AST available at runtime would be interesting. On the other hand, I feel like this particular situation may be better handled in Rust proper.

struct AccordionPageTemplate {
    data: String,
}
struct AccordionPage {
    template: AccordionPageTemplate,
    index: usize,
}
impl Render for AccordionPage {
    fn render(&self) -> Markup {
        html! {
            div my-acccordion-index=(self.index) {
                {"my data is " (self.template.data)}
            }
        }
    }
}

fn accordion(page_templates: Vec<AccordionPageTemplate>) -> Markup {
    html! {
        div my-attribute-type="accordion" {
            @for (index, template) in page_templates.into_iter().enumerate() {
                (AccordionPage { template, index })
            }
        }
    }
}

fn main() -> Result<(), Box<dyn std::error::Error>> {
    let accordion = accordion(vec![
        AccordionPageTemplate {
            data: "First".into(),
        },
        AccordionPageTemplate {
            data: "Second".into(),
        },
    ]);
    println!("{}", accordion.render().into_string());

    Ok(())
}
<div my-attribute-type="accordion"><div my-acccordion-index="0">my data is First</div><div my-acccordion-index="1">my data is Second</div></div>

What I wonder is: given the power and flexibility of Rust's type system, is there any situation where being able to use maud’s AST would solve a problem that couldn't be solved by implementing Render on the right types?

@yatesco
Copy link
Author

yatesco commented Mar 20, 2023

Thanks @rben01 - you make a compelling point. I'm nervous about getting too Java-esque with classes everywhere, but I can't fully articulate why yet. I need to let this simmer in the back of my mind for a bit :-)

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

No branches or pull requests

3 participants