terrazzo-client 0.2.8

A simple macro to clone variables before passing them into a `move` closure or async block.
Documentation
#[cfg(not(feature = "concise-traces"))]
mod verbose {
    use std::ops::DerefMut;

    use crate::attribute::value::XAttributeValue;
    use crate::element::XElement;
    use crate::element::XElementValue;
    use crate::element::template::XTemplate;
    use crate::node::XNode;
    use crate::template::IsTemplate as _;

    impl std::fmt::Debug for XElement {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            let mut b = DebugStringBuilder::default();
            self.print_debug(&mut b);
            write!(f, "{}", b.buffer)
        }
    }

    #[derive(Default)]
    struct DebugStringBuilder {
        padding: String,
        buffer: String,
    }

    impl DebugStringBuilder {
        fn writeln(&mut self, s: impl std::fmt::Display) -> &mut Self {
            self.buffer += &format!("{}{s}\n", self.padding);
            self
        }

        fn indent(&mut self, count: usize) -> impl DerefMut<Target = &mut DebugStringBuilder> {
            self.padding += &String::from_iter(std::iter::repeat_n(' ', count));
            scopeguard::guard(self, move |b| {
                b.padding.drain((b.padding.len() - count)..);
            })
        }
    }

    trait PrintDebug {
        fn print_debug(&self, b: &mut DebugStringBuilder);
    }

    impl PrintDebug for XElement {
        fn print_debug(&self, b: &mut DebugStringBuilder) {
            let mut rest = String::new();
            match &self.value {
                XElementValue::Static {
                    attributes, events, ..
                } => {
                    for attribute in attributes {
                        let attribute_id = &attribute.id;
                        match &attribute.value {
                            XAttributeValue::Null => rest += &format!(" {attribute_id}=null"),
                            XAttributeValue::Static(value) => {
                                rest += &format!(" {attribute_id}={value:?}")
                            }
                            XAttributeValue::Dynamic { .. } => {
                                rest += &format!(" {attribute_id}=<Dynamic>")
                            }
                            XAttributeValue::Generated { .. } => {
                                rest += &format!(" {attribute_id}=<Generated>")
                            }
                        }
                    }
                    for event in events {
                        rest += &format!(" on:{}=[callback]", event.event_type);
                    }
                }
                XElementValue::Dynamic { .. } => (),
                XElementValue::Generated { .. } => (),
            }
            b.writeln(format!(
                "<{tag_name} key={key:?}{rest}>",
                tag_name = self.tag_name.as_deref().unwrap_or("tag"),
                key = self.key
            ));
            {
                let mut b = b.indent(4);
                match &self.value {
                    XElementValue::Static { children, .. } => {
                        for child in children {
                            child.print_debug(&mut b);
                        }
                    }
                    XElementValue::Dynamic { .. } => {
                        b.writeln("[dynamic]");
                    }
                    XElementValue::Generated { template, .. } => {
                        b.writeln(format!("<template {}>", template.debug_id()));
                        {
                            let mut b = b.indent(4);
                            template.print_debug(&mut b);
                        }
                        b.writeln(format!("</template {}>", template.debug_id()));
                    }
                }
            }
            b.writeln(format!(
                "</{tag_name}>",
                tag_name = self.tag_name.as_deref().unwrap_or("tag")
            ));
        }
    }

    impl PrintDebug for XNode {
        fn print_debug(&self, b: &mut DebugStringBuilder) {
            match self {
                XNode::Element(xelement) => {
                    xelement.print_debug(b);
                }
                XNode::Text(xtext) => {
                    b.writeln(format!("{:?}", xtext.0.to_string()));
                }
            }
        }
    }

    impl PrintDebug for XTemplate {
        fn print_debug(&self, b: &mut DebugStringBuilder) {
            self.with_old(|element| {
                if let Some(element) = element {
                    element.print_debug(b);
                } else {
                    b.writeln("[empty]");
                }
            });
        }
    }

    #[cfg(test)]
    mod tests {
        use terrazzo_macro::html;
        use terrazzo_macro::template;

        use crate::prelude::*;

        #[test]
        fn element() {
            #[html]
            fn html() -> XElement {
                div(
                    key = "root",
                    class = "root-css-style",
                    "Text",
                    ul(
                        li(key = "1", "First"),
                        li(key = "2", "Second"),
                        li(key = "3", "Third"),
                    ),
                    p(|t| child(t)),
                    data_dyn_attribute %= dyn_attribute(),
                )
            }

            #[html]
            #[template]
            fn child() -> XElement {
                div(key = "child", class = "child-css-style", span("Child"))
            }

            #[template(wrap = true)]
            fn dyn_attribute() -> XAttributeValue {
                "custom-value"
            }

            let expected = r#"
<div key='root' data-dyn-attribute 0:0=<Dynamic> class 1:0=Str("root-css-style")>
    "Text"
    <ul key=#0>
        <li key='1'>
            "First"
        </li>
        <li key='2'>
            "Second"
        </li>
        <li key='3'>
            "Third"
        </li>
    </ul>
    <p key=#0>
        [dynamic]
    </p>
</div>"#;
            let actual = format!("{:?}", html());
            if expected.trim() != actual.trim() {
                println!("{}", actual);
                panic!();
            }
        }
    }
}

#[cfg(feature = "concise-traces")]
mod concise {
    use crate::element::XELEMENT;
    use crate::element::XElement;

    impl std::fmt::Debug for XElement {
        fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
            f.debug_struct(XELEMENT)
                .field("key", &self.key)
                .field("tag_name", &self.tag_name)
                .finish()
        }
    }

    #[cfg(test)]
    mod tests {
        use terrazzo_macro as _;
    }
}