Repository: jdrouet/mrml (language: rust)

Issue:
Support parsing elements with no closing tag 
I am trying to parse MJML that includes `<br>` tags, but I get the following error: `invalid format`. JavaScript parser for MJML doesn't seem to have to this issue. For example, the following MJML renders without any issues on https://mjml.io/try-it-live:

```
<mjml>
  <mj-body>
    <mj-section>
      <mj-column>
        <mj-text><br>Some Test Content</mj-text>
      </mj-column>
    </mj-section>
  </mj-body>
</mjml>
```

It works when I replace `<br>` with `<br/>` but I am trying to parse some external templates and in some of these templates, `<br>` tags have additional data attributes, so I can't do a simple find and replace.

Please let me know if this can be supported.


Discussion / hints:
Hi! Thank you for your issue.
This comes from the library I use to parse the template: `xmlparser`. `<br>` without a closing `</br>` is not a valid XML element BUT is still valid in HTML.
To fix that bug, I'd need to use a completely different XML parser which is out of scope for now.
Alright! Thanks for letting me know.
@jdrouet Thanks for the amazing crate! ❤️ Would love to pick your brain a bit more on this:

1. As per the [MJML docs](https://documentation.mjml.io/#mj-text), only `mj-raw` and `mj-text` tags allow HTML. Would it be better to:
  (a) use an HTML parser only for the children of `mj-raw` & `mj-text`? (OR)
  (b) replace `xmlparser` completely with an HTML parser?
2. Would [html5ever](https://github.com/servo/html5ever) be a good HTML parser to use?
3. Would you be open to merging a pull request with these changes?
 
Appreciate your time.
Bump, Imo i think there's valid need for these. If @jdrouet can propose a direction, maybe migrating to a different parser can be work on :) 

Fix patch:
diff --git a/packages/mrml-core/src/mj_wrapper/parse.rs b/packages/mrml-core/src/mj_wrapper/parse.rs
index 6349f920..9399bf13 100644
--- a/packages/mrml-core/src/mj_wrapper/parse.rs
+++ b/packages/mrml-core/src/mj_wrapper/parse.rs
@@ -25,3 +25,18 @@ impl Parsable for MJWrapper {
         MJWrapperParser::default().parse(tokenizer)?.build()
     }
 }
+
+#[cfg(test)]
+mod tests {
+    use super::MJWrapper;
+    use crate::prelude::parse::Parsable;
+
+    #[test]
+    fn parse_br_element() {
+        let content = "<mj-wrapper><h1>hello</h1><br><h2>world</h2></mj-wrapper>";
+        let mut tokenizer = xmlparser::Tokenizer::from(content);
+        let _ = tokenizer.next().unwrap();
+        let tag = xmlparser::StrSpan::from("<mj-wrapper");
+        MJWrapper::parse(tag, &mut tokenizer).unwrap();
+    }
+}
diff --git a/packages/mrml-core/src/node/parse.rs b/packages/mrml-core/src/node/parse.rs
index 45512d67..d3dcd60f 100644
--- a/packages/mrml-core/src/node/parse.rs
+++ b/packages/mrml-core/src/node/parse.rs
@@ -26,6 +26,10 @@ where
         Ok(self.0)
     }
 
+    fn should_ignore_children(&self) -> bool {
+        matches!(self.0.tag.as_str(), "br" | "meta")
+    }
+
     parse_attribute!();
 
     fn parse_child_element<'a>(
diff --git a/packages/mrml-core/src/prelude/parse.rs b/packages/mrml-core/src/prelude/parse.rs
index f5dbb0ba..a44d1d44 100644
--- a/packages/mrml-core/src/prelude/parse.rs
+++ b/packages/mrml-core/src/prelude/parse.rs
@@ -98,7 +98,7 @@ impl ToString for Error {
     }
 }
 
-pub fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result<Token<'a>, Error> {
+pub(crate) fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result<Token<'a>, Error> {
     if let Some(token) = tokenizer.next() {
         Ok(token?)
     } else {
@@ -106,7 +106,7 @@ pub fn next_token<'a>(tokenizer: &mut Tokenizer<'a>) -> Result<Token<'a>, Error>
     }
 }
 
-pub fn is_element_start(token: &Token) -> bool {
+pub(crate) fn is_element_start(token: &Token) -> bool {
     matches!(
         token,
         Token::ElementStart {
@@ -122,6 +122,12 @@ pub trait Parser: Sized {
 
     fn build(self) -> Result<Self::Output, Error>;
 
+    /// for elements like <br> or <meta>, they have no closing elements and
+    /// the parser should not check for children
+    fn should_ignore_children(&self) -> bool {
+        false
+    }
+
     fn parse_attribute<'a>(&mut self, name: StrSpan<'a>, _value: StrSpan<'a>) -> Result<(), Error> {
         Err(Error::UnexpectedAttribute(name.start()))
     }
@@ -185,7 +191,9 @@ pub trait Parser: Sized {
                             return Ok(self);
                         }
                         xmlparser::ElementEnd::Open => {
-                            self.parse_children(tokenizer)?;
+                            if !self.should_ignore_children() {
+                                self.parse_children(tokenizer)?;
+                            }
                             return Ok(self);
                         }
                         // unexpected


Test patch: