1 /*! 2 A simple, streaming, partially-validating XML writer that writes XML data into an internal buffer. 3 4 ## Features 5 6 - A simple, bare-minimum, panic-based API. 7 - Non-allocating API. All methods are accepting either `fmt::Display` or `fmt::Arguments`. 8 - Nodes auto-closing. 9 10 ## Example 11 12 ```rust 13 use xmlwriter::*; 14 15 let opt = Options { 16 use_single_quote: true, 17 ..Options::default() 18 }; 19 20 let mut w = XmlWriter::new(opt); 21 w.start_element("svg"); 22 w.write_attribute("xmlns", "http://www.w3.org/2000/svg"); 23 w.write_attribute_fmt("viewBox", format_args!("{} {} {} {}", 0, 0, 128, 128)); 24 w.start_element("text"); 25 // We can write any object that implements `fmt::Display`. 26 w.write_attribute("x", &10); 27 w.write_attribute("y", &20); 28 w.write_text_fmt(format_args!("length is {}", 5)); 29 30 assert_eq!(w.end_document(), 31 "<svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 128 128'> 32 <text x='10' y='20'> 33 length is 5 34 </text> 35 </svg> 36 "); 37 ``` 38 */ 39 40 #![doc(html_root_url = "https://docs.rs/xmlwriter/0.1.0")] 41 42 #![forbid(unsafe_code)] 43 #![warn(missing_docs)] 44 #![warn(missing_copy_implementations)] 45 46 47 use std::fmt::{self, Display}; 48 use std::io::Write; 49 use std::ops::Range; 50 51 52 /// An XML node indention. 53 #[derive(Clone, Copy, PartialEq, Debug)] 54 pub enum Indent { 55 /// Disable indention and new lines. 56 None, 57 /// Indent with spaces. Preferred range is 0..4. 58 Spaces(u8), 59 /// Indent with tabs. 60 Tabs, 61 } 62 63 /// An XML writing options. 64 #[derive(Clone, Copy, Debug)] 65 pub struct Options { 66 /// Use single quote marks instead of double quote. 67 /// 68 /// # Examples 69 /// 70 /// Before: 71 /// 72 /// ```text 73 /// <rect fill="red"/> 74 /// ``` 75 /// 76 /// After: 77 /// 78 /// ```text 79 /// <rect fill='red'/> 80 /// ``` 81 /// 82 /// Default: disabled 83 pub use_single_quote: bool, 84 85 /// Set XML nodes indention. 86 /// 87 /// # Examples 88 /// 89 /// `Indent::None` 90 /// Before: 91 /// 92 /// ```text 93 /// <svg> 94 /// <rect fill="red"/> 95 /// </svg> 96 /// ``` 97 /// 98 /// After: 99 /// 100 /// ```text 101 /// <svg><rect fill="red"/></svg> 102 /// ``` 103 /// 104 /// Default: 4 spaces 105 pub indent: Indent, 106 107 /// Set XML attributes indention. 108 /// 109 /// # Examples 110 /// 111 /// `Indent::Spaces(2)` 112 /// 113 /// Before: 114 /// 115 /// ```text 116 /// <svg> 117 /// <rect fill="red" stroke="black"/> 118 /// </svg> 119 /// ``` 120 /// 121 /// After: 122 /// 123 /// ```text 124 /// <svg> 125 /// <rect 126 /// fill="red" 127 /// stroke="black"/> 128 /// </svg> 129 /// ``` 130 /// 131 /// Default: `None` 132 pub attributes_indent: Indent, 133 } 134 135 impl Default for Options { 136 #[inline] default() -> Self137 fn default() -> Self { 138 Options { 139 use_single_quote: false, 140 indent: Indent::Spaces(4), 141 attributes_indent: Indent::None, 142 } 143 } 144 } 145 146 147 #[derive(Clone, Copy, PartialEq, Debug)] 148 enum State { 149 Empty, 150 Document, 151 Attributes, 152 } 153 154 struct DepthData { 155 range: Range<usize>, 156 has_children: bool, 157 } 158 159 160 /// An XML writer. 161 pub struct XmlWriter { 162 buf: Vec<u8>, 163 state: State, 164 preserve_whitespaces: bool, 165 depth_stack: Vec<DepthData>, 166 opt: Options, 167 } 168 169 impl XmlWriter { 170 #[inline] from_vec(buf: Vec<u8>, opt: Options) -> Self171 fn from_vec(buf: Vec<u8>, opt: Options) -> Self { 172 XmlWriter { 173 buf, 174 state: State::Empty, 175 preserve_whitespaces: false, 176 depth_stack: Vec::new(), 177 opt, 178 } 179 } 180 181 /// Creates a new `XmlWriter`. 182 #[inline] new(opt: Options) -> Self183 pub fn new(opt: Options) -> Self { 184 Self::from_vec(Vec::new(), opt) 185 } 186 187 /// Creates a new `XmlWriter` with a specified capacity. 188 #[inline] with_capacity(capacity: usize, opt: Options) -> Self189 pub fn with_capacity(capacity: usize, opt: Options) -> Self { 190 Self::from_vec(Vec::with_capacity(capacity), opt) 191 } 192 193 /// Writes an XML declaration. 194 /// 195 /// `<?xml version="1.0" encoding="UTF-8" standalone="no"?>` 196 /// 197 /// # Panics 198 /// 199 /// - When called twice. 200 #[inline(never)] write_declaration(&mut self)201 pub fn write_declaration(&mut self) { 202 if self.state != State::Empty { 203 panic!("declaration was already written"); 204 } 205 206 // Pretend that we are writing an element. 207 self.state = State::Attributes; 208 209 // <?xml version='1.0' encoding='UTF-8' standalone='yes'?> 210 self.push_str("<?xml"); 211 self.write_attribute("version", "1.0"); 212 self.write_attribute("encoding", "UTF-8"); 213 self.write_attribute("standalone", "no"); 214 self.push_str("?>"); 215 216 self.state = State::Document; 217 } 218 219 /// Writes a comment string. write_comment(&mut self, text: &str)220 pub fn write_comment(&mut self, text: &str) { 221 self.write_comment_fmt(format_args!("{}", text)); 222 } 223 224 /// Writes a formatted comment. 225 #[inline(never)] write_comment_fmt(&mut self, fmt: fmt::Arguments)226 pub fn write_comment_fmt(&mut self, fmt: fmt::Arguments) { 227 if self.state == State::Attributes { 228 self.write_open_element(); 229 } 230 231 if self.state != State::Empty { 232 self.write_new_line(); 233 } 234 235 self.write_node_indent(); 236 237 // <!--text--> 238 self.push_str("<!--"); 239 self.buf.write_fmt(fmt).unwrap(); // TODO: check content 240 self.push_str("-->"); 241 242 if self.state == State::Attributes { 243 self.depth_stack.push(DepthData { 244 range: 0..0, 245 has_children: false, 246 }); 247 } 248 249 self.state = State::Document; 250 } 251 252 /// Starts writing a new element. 253 /// 254 /// This method writes only the `<tag-name` part. 255 #[inline(never)] start_element(&mut self, name: &str)256 pub fn start_element(&mut self, name: &str) { 257 if self.state == State::Attributes { 258 self.write_open_element(); 259 } 260 261 if self.state != State::Empty { 262 self.write_new_line(); 263 } 264 265 if !self.preserve_whitespaces { 266 self.write_node_indent(); 267 } 268 269 self.push_byte(b'<'); 270 let start = self.buf.len(); 271 self.push_str(name); 272 273 self.depth_stack.push(DepthData { 274 range: start..self.buf.len(), 275 has_children: false, 276 }); 277 278 self.state = State::Attributes; 279 } 280 281 /// Writes an attribute. 282 /// 283 /// Quotes in the value will be escaped. 284 /// 285 /// # Panics 286 /// 287 /// - When called before `start_element()`. 288 /// - When called after `close_element()`. 289 /// 290 /// # Example 291 /// 292 /// ``` 293 /// use xmlwriter::*; 294 /// 295 /// let mut w = XmlWriter::new(Options::default()); 296 /// w.start_element("svg"); 297 /// w.write_attribute("x", "5"); 298 /// w.write_attribute("y", &5); 299 /// assert_eq!(w.end_document(), "<svg x=\"5\" y=\"5\"/>\n"); 300 /// ``` write_attribute<V: Display + ?Sized>(&mut self, name: &str, value: &V)301 pub fn write_attribute<V: Display + ?Sized>(&mut self, name: &str, value: &V) { 302 self.write_attribute_fmt(name, format_args!("{}", value)); 303 } 304 305 /// Writes a formatted attribute value. 306 /// 307 /// Quotes in the value will be escaped. 308 /// 309 /// # Panics 310 /// 311 /// - When called before `start_element()`. 312 /// - When called after `close_element()`. 313 /// 314 /// # Example 315 /// 316 /// ``` 317 /// use xmlwriter::*; 318 /// 319 /// let mut w = XmlWriter::new(Options::default()); 320 /// w.start_element("rect"); 321 /// w.write_attribute_fmt("fill", format_args!("url(#{})", "gradient")); 322 /// assert_eq!(w.end_document(), "<rect fill=\"url(#gradient)\"/>\n"); 323 /// ``` 324 #[inline(never)] write_attribute_fmt(&mut self, name: &str, fmt: fmt::Arguments)325 pub fn write_attribute_fmt(&mut self, name: &str, fmt: fmt::Arguments) { 326 if self.state != State::Attributes { 327 panic!("must be called after start_element()"); 328 } 329 330 self.write_attribute_prefix(name); 331 let start = self.buf.len(); 332 self.buf.write_fmt(fmt).unwrap(); 333 self.escape_attribute_value(start); 334 self.write_quote(); 335 } 336 337 /// Writes a raw attribute value. 338 /// 339 /// Closure provides a mutable reference to an internal buffer. 340 /// 341 /// **Warning:** this method is an escape hatch for cases when you need to write 342 /// a lot of data very fast. 343 /// 344 /// # Panics 345 /// 346 /// - When called before `start_element()`. 347 /// - When called after `close_element()`. 348 /// 349 /// # Example 350 /// 351 /// ``` 352 /// use xmlwriter::*; 353 /// 354 /// let mut w = XmlWriter::new(Options::default()); 355 /// w.start_element("path"); 356 /// w.write_attribute_raw("d", |buf| buf.extend_from_slice(b"M 10 20 L 30 40")); 357 /// assert_eq!(w.end_document(), "<path d=\"M 10 20 L 30 40\"/>\n"); 358 /// ``` 359 #[inline(never)] write_attribute_raw<F>(&mut self, name: &str, f: F) where F: FnOnce(&mut Vec<u8>)360 pub fn write_attribute_raw<F>(&mut self, name: &str, f: F) 361 where F: FnOnce(&mut Vec<u8>) 362 { 363 if self.state != State::Attributes { 364 panic!("must be called after start_element()"); 365 } 366 367 self.write_attribute_prefix(name); 368 let start = self.buf.len(); 369 f(&mut self.buf); 370 self.escape_attribute_value(start); 371 self.write_quote(); 372 } 373 374 #[inline(never)] write_attribute_prefix(&mut self, name: &str)375 fn write_attribute_prefix(&mut self, name: &str) { 376 if self.opt.attributes_indent == Indent::None { 377 self.push_byte(b' '); 378 } else { 379 self.push_byte(b'\n'); 380 381 let depth = self.depth_stack.len(); 382 if depth > 0 { 383 self.write_indent(depth - 1, self.opt.indent); 384 } 385 386 self.write_indent(1, self.opt.attributes_indent); 387 } 388 389 self.push_str(name); 390 self.push_byte(b'='); 391 self.write_quote(); 392 } 393 394 /// Escapes the attribute value string. 395 /// 396 /// - " -> " 397 /// - ' -> ' 398 #[inline(never)] escape_attribute_value(&mut self, mut start: usize)399 fn escape_attribute_value(&mut self, mut start: usize) { 400 let quote = if self.opt.use_single_quote { b'\'' } else { b'"' }; 401 while let Some(idx) = self.buf[start..].iter().position(|c| *c == quote) { 402 let i = start + idx; 403 let s = if self.opt.use_single_quote { b"'" } else { b""" }; 404 self.buf.splice(i..i+1, s.iter().cloned()); 405 start = i + 6; 406 } 407 } 408 409 /// Sets the preserve whitespaces flag. 410 /// 411 /// - If set, text nodes will be written as is. 412 /// - If not set, text nodes will be indented. 413 /// 414 /// Can be set at any moment. 415 /// 416 /// # Example 417 /// 418 /// ``` 419 /// use xmlwriter::*; 420 /// 421 /// let mut w = XmlWriter::new(Options::default()); 422 /// w.start_element("html"); 423 /// w.start_element("p"); 424 /// w.write_text("text"); 425 /// w.end_element(); 426 /// w.start_element("p"); 427 /// w.set_preserve_whitespaces(true); 428 /// w.write_text("text"); 429 /// w.end_element(); 430 /// w.set_preserve_whitespaces(false); 431 /// assert_eq!(w.end_document(), 432 /// "<html> 433 /// <p> 434 /// text 435 /// </p> 436 /// <p>text</p> 437 /// </html> 438 /// "); 439 /// ``` set_preserve_whitespaces(&mut self, preserve: bool)440 pub fn set_preserve_whitespaces(&mut self, preserve: bool) { 441 self.preserve_whitespaces = preserve; 442 } 443 444 /// Writes a text node. 445 /// 446 /// See `write_text_fmt()` for details. write_text(&mut self, text: &str)447 pub fn write_text(&mut self, text: &str) { 448 self.write_text_fmt(format_args!("{}", text)); 449 } 450 451 /// Writes a formatted text node. 452 /// 453 /// `<` will be escaped. 454 /// 455 /// # Panics 456 /// 457 /// - When called not after `start_element()`. 458 #[inline(never)] write_text_fmt(&mut self, fmt: fmt::Arguments)459 pub fn write_text_fmt(&mut self, fmt: fmt::Arguments) { 460 if self.state == State::Empty || self.depth_stack.is_empty() { 461 panic!("must be called after start_element()"); 462 } 463 464 if self.state == State::Attributes { 465 self.write_open_element(); 466 } 467 468 if self.state != State::Empty { 469 self.write_new_line(); 470 } 471 472 self.write_node_indent(); 473 474 let start = self.buf.len(); 475 self.buf.write_fmt(fmt).unwrap(); 476 self.escape_text(start); 477 478 if self.state == State::Attributes { 479 self.depth_stack.push(DepthData { 480 range: 0..0, 481 has_children: false, 482 }); 483 } 484 485 self.state = State::Document; 486 } 487 escape_text(&mut self, mut start: usize)488 fn escape_text(&mut self, mut start: usize) { 489 while let Some(idx) = self.buf[start..].iter().position(|c| *c == b'<') { 490 let i = start + idx; 491 self.buf.splice(i..i+1, b"<".iter().cloned()); 492 start = i + 4; 493 } 494 } 495 496 /// Closes an open element. 497 #[inline(never)] end_element(&mut self)498 pub fn end_element(&mut self) { 499 if let Some(depth) = self.depth_stack.pop() { 500 if depth.has_children { 501 if !self.preserve_whitespaces { 502 self.write_new_line(); 503 self.write_node_indent(); 504 } 505 506 self.push_str("</"); 507 508 for i in depth.range { 509 self.push_byte(self.buf[i]); 510 } 511 512 self.push_byte(b'>'); 513 } else { 514 self.push_str("/>"); 515 } 516 } 517 518 self.state = State::Document; 519 } 520 521 /// Closes all open elements and returns an internal XML buffer. 522 /// 523 /// # Example 524 /// 525 /// ``` 526 /// use xmlwriter::*; 527 /// 528 /// let mut w = XmlWriter::new(Options::default()); 529 /// w.start_element("svg"); 530 /// w.start_element("g"); 531 /// w.start_element("rect"); 532 /// assert_eq!(w.end_document(), 533 /// "<svg> 534 /// <g> 535 /// <rect/> 536 /// </g> 537 /// </svg> 538 /// "); 539 /// ``` end_document(mut self) -> String540 pub fn end_document(mut self) -> String { 541 while !self.depth_stack.is_empty() { 542 self.end_element(); 543 } 544 545 self.write_new_line(); 546 547 // The only way it can fail is if an invalid data 548 // was written via `write_attribute_raw()`. 549 String::from_utf8(self.buf).unwrap() 550 } 551 552 #[inline] push_byte(&mut self, c: u8)553 fn push_byte(&mut self, c: u8) { 554 self.buf.push(c); 555 } 556 557 #[inline] push_str(&mut self, text: &str)558 fn push_str(&mut self, text: &str) { 559 self.buf.extend_from_slice(text.as_bytes()); 560 } 561 562 #[inline] get_quote_char(&self) -> u8563 fn get_quote_char(&self) -> u8 { 564 if self.opt.use_single_quote { b'\'' } else { b'"' } 565 } 566 567 #[inline] write_quote(&mut self)568 fn write_quote(&mut self) { 569 self.push_byte(self.get_quote_char()); 570 } 571 write_open_element(&mut self)572 fn write_open_element(&mut self) { 573 if let Some(depth) = self.depth_stack.last_mut() { 574 depth.has_children = true; 575 self.push_byte(b'>'); 576 577 self.state = State::Document; 578 } 579 } 580 write_node_indent(&mut self)581 fn write_node_indent(&mut self) { 582 self.write_indent(self.depth_stack.len(), self.opt.indent); 583 } 584 write_indent(&mut self, depth: usize, indent: Indent)585 fn write_indent(&mut self, depth: usize, indent: Indent) { 586 if indent == Indent::None || self.preserve_whitespaces { 587 return; 588 } 589 590 for _ in 0..depth { 591 match indent { 592 Indent::None => {} 593 Indent::Spaces(n) => { 594 for _ in 0..n { 595 self.push_byte(b' '); 596 } 597 } 598 Indent::Tabs => self.push_byte(b'\t'), 599 } 600 } 601 } 602 write_new_line(&mut self)603 fn write_new_line(&mut self) { 604 if self.opt.indent != Indent::None && !self.preserve_whitespaces { 605 self.push_byte(b'\n'); 606 } 607 } 608 } 609