docx-reader 0.1.1

A .docx file reader in rust
Documentation
use std::io::Read;
use std::str::FromStr;
use xml::attribute::OwnedAttribute;
use xml::reader::{EventReader, XmlEvent};

use crate::escape::replace_escaped;
use crate::types::BreakType;
use crate::{reader::*, FieldCharType};

use super::Run;

#[derive(PartialEq, Debug)]
enum TextState {
	Idle,
	Text,
	Delete,
}

fn read_field_char(attributes: &[OwnedAttribute]) -> Result<FieldChar, ReaderError> {
	let mut t: Option<FieldCharType> = None;
	let mut dirty = false;
	for a in attributes {
		let local_name = &a.name.local_name;
		match local_name.as_str() {
			"fldCharType" => {
				if let Ok(ty) = FieldCharType::from_str(&a.value) {
					t = Some(ty);
				}
			}
			"dirty" => {
				dirty = !is_false(&a.value);
			}
			_ => {}
		}
	}

	if let Some(t) = t {
		let mut f = FieldChar::new(t);
		if dirty {
			f = f.dirty();
		}
		Ok(f)
	} else {
		Err(ReaderError::XMLReadError)
	}
}

impl ElementReader for Run {
	fn read<R: Read>(
		r: &mut EventReader<R>,
		_attrs: &[OwnedAttribute],
	) -> Result<Self, ReaderError> {
		let mut run = Run::new();
		let mut text_state = TextState::Idle;
		loop {
			let e = r.next();
			match e {
				Ok(XmlEvent::StartElement {
					attributes, name, ..
				}) => {
					match name.prefix.as_deref() {
						Some("w") => {
							let e = XMLElement::from_str(&name.local_name).unwrap();

							ignore::ignore_element(e.clone(), XMLElement::RunPropertyChange, r);

							match e {
								XMLElement::Tab => {
									run = run.add_tab();
								}
								XMLElement::Sym => {
									if let Some(font) = read(&attributes, "font") {
										if let Some(char) = read(&attributes, "char") {
											let sym = Sym::new(font, char);
											run = run.add_sym(sym);
										}
									}
								}
								XMLElement::RunProperty => {
									let p = RunProperty::read(r, &attributes)?;
									run = run.set_property(p);
								}
								XMLElement::Text => text_state = TextState::Text,
								XMLElement::DeleteText => text_state = TextState::Delete,
								XMLElement::Break => {
									if let Some(a) = &attributes.get(0) {
										run = run.add_break(BreakType::from_str(&a.value)?)
									} else {
										run = run.add_break(BreakType::TextWrapping)
									}
								}
								XMLElement::Drawing => {
									if let Ok(drawing) = Drawing::read(r, &attributes) {
										run = run.add_drawing(drawing);
									}
								}
								XMLElement::FieldChar => {
									if let Ok(f) = read_field_char(&attributes) {
										run.children.push(RunChild::FieldChar(f));
									}
								}
								XMLElement::InstrText => loop {
									let e = r.next();
									match e {
										Ok(XmlEvent::Characters(c)) => {
											run.children.push(RunChild::InstrTextString(c));
											break;
										}
										Ok(XmlEvent::EndElement { name, .. }) => {
											let e = XMLElement::from_str(&name.local_name).unwrap();
											match e {
												XMLElement::Run => {
													return Ok(run);
												}
												_ => {}
											}
										}
										Err(_) => return Err(ReaderError::XMLReadError),
										_ => {}
									}
								},
								_ => {}
							}
						}
						Some("mc") => {
							let e = McXMLElement::from_str(&name.local_name).unwrap();
							match e {
								McXMLElement::Fallback => {
									let _ = McFallback::read(r, &attributes)?;
								}
								_ => {}
							}
						}
						Some("v") => {
							let e = VXMLElement::from_str(&name.local_name).unwrap();
							match e {
								// Experimental For now support only imageData in shape
								VXMLElement::Shape => {
									if let Ok(shape) = Shape::read(r, &attributes) {
										run.children.push(RunChild::Shape(Box::new(shape)));
									}
								}
								_ => {}
							}
						}
						_ => {}
					};
				}
				Ok(XmlEvent::Characters(c)) => match text_state {
					TextState::Delete => {
						run = run.add_delete_text_without_escape(replace_escaped(&c));
					}
					TextState::Text => {
						run = run.add_text_without_escape(replace_escaped(&c));
					}
					_ => {}
				},
				Ok(XmlEvent::Whitespace(c)) => match text_state {
					TextState::Delete => {
						run = run.add_delete_text_without_escape(replace_escaped(&c));
					}
					TextState::Text => {
						run = run.add_text_without_escape(replace_escaped(&c));
					}
					_ => {}
				},
				Ok(XmlEvent::EndElement { name, .. }) => {
					let e = XMLElement::from_str(&name.local_name).unwrap();
					match e {
						XMLElement::Run => {
							return Ok(run);
						}
						XMLElement::DeleteText | XMLElement::Text => text_state = TextState::Idle,
						_ => {}
					}
				}
				Err(_) => return Err(ReaderError::XMLReadError),
				_ => {}
			}
		}
	}
}