kotlin_poet_rs/spec/
kdoc.rs

1use crate::io::RenderKotlin;
2use crate::spec::CodeBlock;
3use crate::tokens;
4
5/// Represents a Kotlin documentation comment in KDoc format.
6///
7/// Entities that support KDoc should usually store it as singular instance and merge multiple KDocs into one.
8#[derive(Debug, Clone)]
9pub struct KDoc {
10    content: String,
11}
12
13impl KDoc {
14    /// Creates new, empty KDoc
15    pub fn new() -> KDoc {
16        KDoc {
17            content: String::new()
18        }
19    }
20
21    /// Appends content to the KDoc.
22    pub fn append(mut self, content: &str) -> KDoc {
23        self.content.push_str(content);
24        self
25    }
26
27    /// Merges contents of [other] into [self]. Adds new line between contents.
28    pub fn merge(mut self, other: KDoc) -> KDoc {
29        self.content.push_str(tokens::NEW_LINE);
30        self.content.push_str(other.content.as_str());
31        self
32    }
33}
34
35impl From<&str> for KDoc {
36    fn from(value: &str) -> Self {
37        KDoc::new().append(value)
38    }
39}
40
41impl RenderKotlin for KDoc {
42    fn render_into(&self, block: &mut CodeBlock) {
43        block.push_static_atom(tokens::KDOC_COMMENT_START);
44        block.push_new_line();
45        let split = self.content.split(tokens::NEW_LINE)
46            .enumerate().collect::<Vec<_>>();
47        let split_len = split.len();
48        for (idx, line) in split {
49            if idx == split_len - 1 && line.is_empty() {
50                break;
51            }
52
53            block.push_static_atom(tokens::KDOC_COMMENT_MIDDLE);
54            block.push_space();
55            block.push_atom(line);
56            block.push_new_line();
57        }
58        block.push_static_atom(tokens::KDOC_COMMENT_END);
59    }
60}
61
62/// Utility wrapper around [Option<KDoc>] that allows to store it as an option and merge multiple KDocs into one.
63#[derive(Default, Clone, Debug)]
64pub(crate) struct KdocSlot(Option<KDoc>);
65impl RenderKotlin for KdocSlot {
66    fn render_into(&self, block: &mut CodeBlock) {
67        if let Some(kdoc) = &self.0 {
68            block.push_renderable(kdoc);
69            block.push_new_line()
70        }
71    }
72}
73
74impl KdocSlot {
75    /// Merges [other] into this KDoc slot.
76    /// If [None] sets [other] as current value.
77    pub(crate) fn merge(&mut self, other: KDoc) {
78        match self.0 {
79            None => { self.0 = Some(other) }
80            Some(ref mut old) => {
81                old.content.push_str(tokens::NEW_LINE);
82                old.content.push_str(other.content.as_str());
83            }
84        };
85    }
86}
87
88macro_rules! mixin_kdoc_mutators {
89    () => {
90        /// Adds [KDoc] to this entity.
91        /// In case of multiple calls, KDocs will be merged, see [KDoc::merge].
92        pub fn kdoc<KDocLike: Into<crate::spec::KDoc>> (mut self, kdoc: KDocLike) -> Self {
93            self.kdoc.merge(kdoc.into());
94            self
95        }
96    };
97}
98
99pub(crate) use mixin_kdoc_mutators;
100
101#[cfg(test)]
102mod tests {
103    use crate::io::RenderKotlin;
104
105    #[test]
106    fn test_kdoc_render() {
107        let comment = super::KDoc::new()
108            .append("Hello\n")
109            .append("World\n");
110
111        assert_eq!(
112            comment.render_string(),
113            "/**\n * Hello\n * World\n */"
114        )
115    }
116
117    #[test]
118    fn test_comment_block() {
119        let comment = super::KDoc::new()
120            .append("Hello\n")
121            .merge(super::KDoc::new().append("World\n"));
122
123        assert_eq!(
124            comment.render_string(),
125            "/**\n * Hello\n * \n * World\n */"
126        )
127    }
128}