docx_rs/documents/elements/
hyperlink.rs1use serde::Serialize;
2use std::io::Write;
3
4use super::*;
5use crate::documents::BuildXML;
6use crate::escape::escape;
7use crate::types::*;
8use crate::{create_hyperlink_rid, generate_hyperlink_id, xml_builder::*};
9
10#[derive(Serialize, Debug, Clone, PartialEq)]
11#[serde(tag = "type")]
12#[serde(rename_all = "camelCase")]
13pub enum HyperlinkData {
14 External {
15 rid: String,
16 #[serde(skip_serializing_if = "String::is_empty")]
18 path: String,
19 },
20 Anchor {
21 anchor: String,
22 },
23}
24
25#[derive(Serialize, Debug, Clone, PartialEq)]
26#[serde(rename_all = "camelCase")]
27pub struct Hyperlink {
28 #[serde(flatten)]
29 pub link: HyperlinkData,
30 pub history: Option<usize>,
31 pub children: Vec<ParagraphChild>,
32}
33
34impl Hyperlink {
35 pub fn new(value: impl Into<String>, t: HyperlinkType) -> Self {
36 let link = {
37 match t {
38 HyperlinkType::External => HyperlinkData::External {
39 rid: create_hyperlink_rid(generate_hyperlink_id()),
40 path: escape(&value.into()),
41 },
42 HyperlinkType::Anchor => HyperlinkData::Anchor {
43 anchor: value.into(),
44 },
45 }
46 };
47 Hyperlink {
48 link,
49 history: None,
50 children: vec![],
51 }
52 }
53
54 pub fn add_run(mut self, run: Run) -> Self {
55 self.children.push(ParagraphChild::Run(Box::new(run)));
56 self
57 }
58
59 pub fn add_structured_data_tag(mut self, t: StructuredDataTag) -> Self {
60 self.children
61 .push(ParagraphChild::StructuredDataTag(Box::new(t)));
62 self
63 }
64
65 pub fn add_insert(mut self, insert: Insert) -> Self {
66 self.children.push(ParagraphChild::Insert(insert));
67 self
68 }
69
70 pub fn add_delete(mut self, delete: Delete) -> Self {
71 self.children.push(ParagraphChild::Delete(delete));
72 self
73 }
74
75 pub fn add_bookmark_start(mut self, id: usize, name: impl Into<String>) -> Self {
76 self.children
77 .push(ParagraphChild::BookmarkStart(BookmarkStart::new(id, name)));
78 self
79 }
80
81 pub fn add_bookmark_end(mut self, id: usize) -> Self {
82 self.children
83 .push(ParagraphChild::BookmarkEnd(BookmarkEnd::new(id)));
84 self
85 }
86
87 pub fn add_comment_start(mut self, comment: Comment) -> Self {
88 self.children.push(ParagraphChild::CommentStart(Box::new(
89 CommentRangeStart::new(comment),
90 )));
91 self
92 }
93
94 pub fn add_comment_end(mut self, id: usize) -> Self {
95 self.children
96 .push(ParagraphChild::CommentEnd(CommentRangeEnd::new(id)));
97 self
98 }
99}
100
101impl BuildXML for Hyperlink {
102 fn build_to<W: Write>(
103 &self,
104 stream: xml::writer::EventWriter<W>,
105 ) -> xml::writer::Result<xml::writer::EventWriter<W>> {
106 XMLBuilder::from(stream)
107 .apply(|b| match self.link {
108 HyperlinkData::Anchor { ref anchor } => b.open_hyperlink(
109 None,
110 Some(anchor.clone()).as_ref(),
111 Some(self.history.unwrap_or(1)),
112 ),
113 HyperlinkData::External { ref rid, .. } => b.open_hyperlink(
114 Some(rid.clone()).as_ref(),
115 None,
116 Some(self.history.unwrap_or(1)),
117 ),
118 })?
119 .add_children(&self.children)?
120 .close()?
121 .into_inner()
122 }
123}
124
125#[cfg(test)]
126mod tests {
127
128 use super::*;
129 #[cfg(test)]
130 use pretty_assertions::assert_eq;
131 use std::str;
132
133 #[test]
134 fn test_hyperlink() {
135 let l = Hyperlink::new("ToC1", HyperlinkType::Anchor).add_run(Run::new().add_text("hello"));
136 let b = l.build();
137 assert_eq!(
138 str::from_utf8(&b).unwrap(),
139 r#"<w:hyperlink w:anchor="ToC1" w:history="1"><w:r><w:rPr /><w:t xml:space="preserve">hello</w:t></w:r></w:hyperlink>"#
140 );
141 }
142}