use super::*; use serde::ser::{SerializeStruct, Serializer}; use serde::Serialize; use crate::documents::BuildXML; use crate::types::BreakType; use crate::xml_builder::*; #[derive(Serialize, Debug, Clone, PartialEq)] #[serde(rename_all = "camelCase")] pub struct Run { pub run_property: RunProperty, pub children: Vec, } impl Default for Run { fn default() -> Self { let run_property = RunProperty::new(); Self { run_property, children: vec![], } } } #[derive(Debug, Clone, PartialEq)] pub enum RunChild { Text(Text), DeleteText(DeleteText), Tab(Tab), Break(Break), Drawing(Box), CommentStart(Box), CommentEnd(CommentRangeEnd), } impl Serialize for RunChild { fn serialize(&self, serializer: S) -> Result where S: Serializer, { match *self { RunChild::Text(ref s) => { let mut t = serializer.serialize_struct("Text", 2)?; t.serialize_field("type", "text")?; t.serialize_field("data", s)?; t.end() } RunChild::DeleteText(ref s) => { let mut t = serializer.serialize_struct("DeleteText", 2)?; t.serialize_field("type", "deleteText")?; t.serialize_field("data", s)?; t.end() } RunChild::Tab(_) => { let mut t = serializer.serialize_struct("Tab", 1)?; t.serialize_field("type", "tab")?; t.end() } RunChild::Break(ref s) => { let mut t = serializer.serialize_struct("Break", 2)?; t.serialize_field("type", "break")?; t.serialize_field("data", s)?; t.end() } RunChild::Drawing(ref s) => { let mut t = serializer.serialize_struct("Drawing", 2)?; t.serialize_field("type", "drawing")?; t.serialize_field("data", s)?; t.end() } RunChild::CommentStart(ref r) => { let mut t = serializer.serialize_struct("CommentRangeStart", 2)?; t.serialize_field("type", "commentRangeStart")?; t.serialize_field("data", r)?; t.end() } RunChild::CommentEnd(ref r) => { let mut t = serializer.serialize_struct("CommentRangeEnd", 2)?; t.serialize_field("type", "commentRangeEnd")?; t.serialize_field("data", r)?; t.end() } } } } impl Run { pub fn new() -> Run { Run { ..Default::default() } } pub fn add_text(mut self, text: impl Into) -> Run { self.children.push(RunChild::Text(Text::new(text))); self } pub fn add_delete_text(mut self, text: impl Into) -> Run { self.children .push(RunChild::DeleteText(DeleteText::new(text))); self } pub fn add_tab(mut self) -> Run { self.children.push(RunChild::Tab(Tab::new())); self } pub fn add_image(mut self, pic: Pic) -> Run { if pic.position_type == DrawingPositionType::Anchor { let pos_h = pic.position_h; let pos_v = pic.position_v; self.children.push(RunChild::Drawing(Box::new( Drawing::new() .pic(pic) .floating() .position_h(pos_h) .position_v(pos_v), ))); } else { self.children .push(RunChild::Drawing(Box::new(Drawing::new().pic(pic)))); } self } // TODO: Remove later pub fn add_drawing(mut self, d: Drawing) -> Run { self.children.push(RunChild::Drawing(Box::new(d))); self } pub fn add_break(mut self, break_type: BreakType) -> Run { self.children.push(RunChild::Break(Break::new(break_type))); self } pub fn size(mut self, size: usize) -> Run { self.run_property = self.run_property.size(size); self } pub fn spacing(mut self, v: i32) -> Run { self.run_property = self.run_property.spacing(v); self } pub fn color(mut self, color: impl Into) -> Run { self.run_property = self.run_property.color(color); self } pub fn highlight(mut self, color: impl Into) -> Run { self.run_property = self.run_property.highlight(color); self } pub fn bold(mut self) -> Run { self.run_property = self.run_property.bold(); self } pub fn disable_bold(mut self) -> Run { self.run_property = self.run_property.disable_bold(); self } pub fn italic(mut self) -> Run { self.run_property = self.run_property.italic(); self } pub fn disable_italic(mut self) -> Run { self.run_property = self.run_property.disable_italic(); self } pub fn underline(mut self, line_type: impl Into) -> Run { self.run_property = self.run_property.underline(line_type); self } pub fn vanish(mut self) -> Run { self.run_property = self.run_property.vanish(); self } pub fn fonts(mut self, f: RunFonts) -> Run { self.run_property = self.run_property.fonts(f); self } } impl BuildXML for Run { fn build(&self) -> Vec { let b = XMLBuilder::new(); let mut b = b.open_run().add_child(&self.run_property); for c in &self.children { match c { RunChild::Text(t) => b = b.add_child(t), RunChild::DeleteText(t) => b = b.add_child(t), RunChild::Tab(t) => b = b.add_child(t), RunChild::Break(t) => b = b.add_child(t), RunChild::Drawing(t) => b = b.add_child(t), RunChild::CommentStart(c) => b = b.add_child(c), RunChild::CommentEnd(c) => b = b.add_child(c), } } b.close().build() } } #[cfg(test)] mod tests { use super::*; #[cfg(test)] use pretty_assertions::assert_eq; use std::str; #[test] fn test_build() { let b = Run::new().add_text("Hello").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_underline() { let b = Run::new().add_text("Hello").underline("single").build(); assert_eq!( str::from_utf8(&b).unwrap(), r#"Hello"# ); } #[test] fn test_child_json() { let c = RunChild::Text(Text::new("Hello")); assert_eq!( serde_json::to_string(&c).unwrap(), r#"{"type":"text","data":{"preserveSpace":true,"text":"Hello"}}"# ); } #[test] fn test_run_json() { let run = Run { children: vec![ RunChild::Tab(Tab::new()), RunChild::Text(Text::new("Hello")), RunChild::Break(Break::new(BreakType::Page)), RunChild::DeleteText(DeleteText::new("deleted")), ], run_property: RunProperty { sz: Some(Sz::new(30)), sz_cs: Some(SzCs::new(30)), color: Some(Color::new("C9211E")), highlight: Some(Highlight::new("yellow")), underline: Some(Underline::new("single")), bold: Some(Bold::new()), bold_cs: Some(BoldCs::new()), italic: Some(Italic::new()), italic_cs: Some(ItalicCs::new()), vanish: Some(Vanish::new()), spacing: Some(100), fonts: None, text_border: None, }, }; assert_eq!( serde_json::to_string(&run).unwrap(), r#"{"runProperty":{"sz":30,"szCs":30,"color":"C9211E","highlight":"yellow","underline":"single","bold":true,"boldCs":true,"italic":true,"italicCs":true,"vanish":true,"spacing":100,"fonts":null,"textBorder":null},"children":[{"type":"tab"},{"type":"text","data":{"preserveSpace":true,"text":"Hello"}},{"type":"break","data":{"breakType":"page"}},{"type":"deleteText","data":{"text":"deleted","preserveSpace":true}}]}"# ); } }