docx_rs/documents/elements/
hyperlink.rs

1use 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        // path is writer only
16        #[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}