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