1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
// Copyright 2021 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

pub use reference_doc_macro::ReferenceDoc;

/// Trait added by using `derive(ReferenceDoc)]`.
pub trait MarkdownReferenceDocGenerator {
    /// Returns a Markdown representation of the reference docs for the
    /// struct that is derived from `ReferenceDoc`. The returned Markdown
    /// indents any `#` Markdown headers in individual field doc comments
    /// to ensure a well structured final Markdown document.
    fn get_reference_doc_markdown() -> String;

    /// This method is called internally by the reference doc generator when
    /// recursing to generate documentation for field types.
    fn get_reference_doc_markdown_with_options(
        indent_headers_by: usize,
        indent_with_spaces: usize,
    ) -> String {
        let doc = Self::get_reference_doc_markdown();
        indent_lines_with_spaces(
            &indent_all_markdown_headers_by(&doc, indent_headers_by),
            indent_with_spaces,
        )
    }
}

/// Helper function to indent markdown headers in `str` by `n` additional hash
/// marks.
fn indent_all_markdown_headers_by(s: &str, n: usize) -> String {
    if n == 0 {
        s.to_string()
    } else {
        s.split('\n').map(|part| indent_markdown_header_by(part, n)).collect::<Vec<_>>().join("\n")
    }
}

fn indent_markdown_header_by(s: &str, n: usize) -> String {
    if s.starts_with("#") {
        "#".to_string().repeat(n) + &s
    } else {
        s.to_string()
    }
}

fn indent_lines_with_spaces(s: &str, n: usize) -> String {
    if n == 0 {
        s.to_string()
    } else {
        let prefix = " ".to_string().repeat(n);
        s.split('\n')
            .map(|part| if part.is_empty() { "".to_string() } else { prefix.clone() + part })
            .collect::<Vec<_>>()
            .join("\n")
    }
}