1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219
/// # `hiccup!`: /// * The main objective of this lib is to prevent unclosed html tags. /// This macro is inspired by Clojures [hiccup](https://github.com/weavejester/hiccup) /// /// ## Basic usage: /// /// The macro `hiccup! receives a mutable string as the first argument and mutates the string to emit the HTML. /// The order of the elements is: /// 1. `tag` as the first element. /// 2. Optional attribute inside the tag should follow the tag name as `{attribute1=>"value1 value2 ... valuen", attr=>"value"}`. Also, the attributes should be inside `{...}` and separate each key value pair by `,`. /// The element should be written as `key=>"value"`, where key is a symbol, followed by an arrow (`=>`), and then the value as a string `"value"`. /// 3. After (Optional) the tag name or the attributes `{...}` you could include `[...]` that can have other tags, such as `p["text"]` or regular string values. /// 4. Inside the `[...]` you also can substitute your string for some simple rust code inside a `(...)`. This can bem something like `p[format!("{:?}", 3 + 4)]` or `div[(x)]` where x was defined in the outside. /// /// ### Differences between Clojure and Rust Hiccup: /// * [Clojure](https://github.com/weavejester/hiccup/wiki/Syntax): `[:a {:href "http://github.com"} "GitHub"]` /// * Rust: `a{href=>"http://github.com"}["GitHub"]` /// /// ### Syntax with code inside: /// * `a{href=>"http://github.com"}[(format!("{:?}", 3 + 5))]` /// * `p[(x)]`, where `x` can be another `html` or simply a value; /// /// ## Example /// /// ### Basic syntax /// ```rust /// use hiccup::hiccup; /// /// fn main() { /// let mut html = String::new(); /// /// let _ = hiccup!(&mut html, /// html[ /// head[meta{name=>"author", content=>"Julia Naomi"} /// title["Hiccup guide"]] /// body{class=>"amazing hiccup guide"}[ /// h1{font=>"bold", color=>"red"}["Hiccup is the best!"] /// p["please lookup clojure's hiccup for better ideas on this macro"]] /// ]); /// /// assert_eq!(html,"<html><head><meta name=\"author\" content=\"Julia Naomi\"/>\ /// <title>Hiccup guide</title></head><body class=\"amazing hiccup guide\">\ /// <h1 font=\"bold\" color=\"red\">Hiccup is the best!</h1>\ /// <p>please lookup clojure\'s hiccup for better ideas on this macro</p></body></html>"); /// } /// ``` /// /// ### With remote code execution /// ```rust /// use hiccup::hiccup; /// /// fn main() { /// let mut html_inner = String::new(); /// let mut html_outer = String::new(); /// let x = "inner my str"; /// let y = "my str2"; /// let z = "my str3"; /// /// let _ = hiccup!(&mut html_inner, /// div[ /// div{hello=>"inner world"}[(x)] /// ] /// ); /// /// let _ = hiccup!(&mut html_outer, /// html[ /// body{class=>"amazing hiccup guide"}[ /// p["please lookup clojure's hiccup for better ideas on this macro"] /// div[ /// div{hello=>"world"}[(html_inner)] /// div[(y.to_owned() + " " + z)] /// p["bye"] /// ] /// ] /// ]); /// /// assert_eq!(html_outer,"<html><body class=\"amazing hiccup guide\">\ /// <p>please lookup clojure\'s hiccup for better ideas on this macro</p>\ /// <div><div hello=\"world\"><div><div hello=\"inner world\">inner my str</div></div></div>\ /// <div>my str2 my str3</div><p>bye</p></div></body></html>"); /// } /// ``` /// ## FAQs /// 1. Is it possible tu use this lib as an XML templating? /// > Yes, I added a more generic XML case to the tests recently /// /// ```rust /// use hiccup::hiccup; /// /// fn main() { /// let mut out = String::new(); /// /// let _ = hiccup!(&mut out, /// xml{metas=>"I forgot them all", version=>"any version"}[ /// family[name{name=>"Rubiechiov", origin=>"Kazakhstan"}] /// members{class=>"close family"}[ /// member{age=>"oldest", color=>"yellow"}["some name"] /// member{age=>"mid-age", color=>"yellow"}["some other name"] /// member{age=>"yougest", color=>"brown"}["Julia"]] /// ]); /// /// assert_eq!(out,"<xml metas=\"I forgot them all\" version=\"any version\"><family>\ /// <name name=\"Rubiechiov\" origin=\"Kazakhstan\"/></family><members class=\"close family\">\ /// <member age=\"oldest\" color=\"yellow\">some name</member>\ /// <member age=\"mid-age\" color=\"yellow\">some other name</member>\ /// <member age=\"yougest\" color=\"brown\">Julia</member></members></xml>"); /// } /// ``` /// #[macro_export] macro_rules! hiccup { ($w:expr, ) => (()); ($w:expr, $e:tt) => {{ use std::fmt::Write; let _ = write!($w, "{}", $e); }}; ($w:expr, $tag:ident {$($key:expr => $value:expr),*}[($($code:block)*) $($inner:tt)*] $($rest:tt)*) => {{ use std::fmt::Write; let _ = write!($w, "<{}", stringify!($tag)); $( let _ = write!($w, " {}=", stringify!($key)); let _ = write!($w, "{}", stringify!($value)); )* let _ = write!($w, ">"); $($code)* hiccup!($w, $($inner)*); let _ = write!($w, "</{}>", stringify!($tag)); hiccup!($w, $($rest)*); }}; ($w:expr, $tag:ident {$($key:expr => $value:expr),*}[$($inner:tt)*] $($rest:tt)*) => {{ use std::fmt::Write; let _ = write!($w, "<{}", stringify!($tag)); $( let _ = write!($w, " {}=", stringify!($key)); let _ = write!($w, "{}", stringify!($value)); )* let _ = write!($w, ">"); hiccup!($w, $($inner)*); let _ = write!($w, "</{}>", stringify!($tag)); hiccup!($w, $($rest)*); }}; ($w:expr, $tag:ident [($($code:block)*) $($inner:tt)*] $($rest:tt)*) => {{ use std::fmt::Write; let _ = write!($w, "<{}>", stringify!($tag)); $($code)* hiccup!($w, $($inner)*); let _ = write!($w, "</{}>", stringify!($tag)); hiccup!($w, $($rest)*); }}; ($w:expr, $tag:ident {$($key:expr => $value:expr),*} $($rest:tt)*) => {{ use std::fmt::Write; let _ = write!($w, "<{}", stringify!($tag)); $( let _ = write!($w, " {}=", stringify!($key)); let _ = write!($w, "{}", stringify!($value)); )* let _ = write!($w, "/>"); hiccup!($w, $($rest)*); }}; ($w:expr, $tag:ident [$($inner:tt)*] $($rest:tt)*) => {{ use std::fmt::Write; let _ = write!($w, "<{}>", stringify!($tag)); hiccup!($w, $($inner)*); let _ = write!($w, "</{}>", stringify!($tag)); hiccup!($w, $($rest)*); }}; ($w:expr, $tag:ident [($($code:block)*)] $($rest:tt)*) => {{ use std::fmt::Write; let _ = write!($w, "<{}>", stringify!($tag)); $($code)* let _ = write!($w, "</{}>", stringify!($tag)); hiccup!($w, $($rest)*); }}; } #[cfg(test)] mod tests { #[test] fn basic_html() { let mut out = String::new(); let _ = hiccup!(&mut out, html[ head[title["Hiccup guide"]] body[h1["Hiccup is the best!"]] ]); assert_eq!(out, "<html><head><title>Hiccup guide</title></head>\ <body><h1>Hiccup is the best!</h1></body></html>"); } #[test] fn attr_block() { let mut out = String::new(); let _ = hiccup!(&mut out, html[ head[title["Hiccup guide"]] body[h1{class=>"value", c=>"v"}["Hiccup is the best!"]] ]); assert_eq!(out, "<html><head><title>Hiccup guide</title></head><body>\ <h1 class=\"value\" c=\"v\">Hiccup is the best!</h1></body></html>"); } }