syncdoc_migrate/
restore.rs

1//! Restore inline documentation from external markdown files
2//!
3//! This module implements the inverse of the migration process, converting
4//! external markdown documentation back into inline Rust doc comments.
5
6mod inject;
7
8use crate::discover::ParsedFile;
9use proc_macro2::TokenStream;
10
11/// Restores inline documentation by reading markdown files and converting omnidoc attributes
12pub fn restore_file(parsed: &ParsedFile, docs_root: &str) -> Option<String> {
13    let transformed = inject::inject_all_doc_comments(&parsed.content, docs_root, parsed);
14
15    crate::rewrite::reformat::rewrite_preserving_format_restore(
16        &parsed.original_source,
17        &transformed.to_string(),
18    )
19    .ok()
20}
21
22pub(crate) fn read_item_markdown(
23    context: &[String],
24    item_name: &str,
25    docs_root: &str,
26) -> Option<String> {
27    let mut path_parts = vec![docs_root.to_string()];
28    path_parts.extend(context.iter().cloned());
29    path_parts.push(format!("{}.md", item_name));
30
31    let md_path = path_parts.join("/");
32    std::fs::read_to_string(&md_path).ok()
33}
34
35pub(crate) fn read_module_doc(parsed: &ParsedFile, docs_root: &str) -> Option<String> {
36    let module_path = syncdoc_core::path_utils::extract_module_path(&parsed.path.to_string_lossy());
37
38    let file_stem = parsed
39        .path
40        .file_stem()
41        .and_then(|s| s.to_str())
42        .unwrap_or("module");
43
44    let md_path = if module_path.is_empty() {
45        format!("{}/{}.md", docs_root, file_stem)
46    } else {
47        format!("{}/{}.md", docs_root, module_path)
48    };
49
50    std::fs::read_to_string(&md_path).ok()
51}
52
53pub(crate) fn generate_doc_comments(content: &str) -> TokenStream {
54    use quote::quote;
55    let lines: Vec<_> = content.trim_end().lines().collect();
56    let mut output = TokenStream::new();
57
58    for line in lines {
59        let comment = format!("/// {}", line);
60        output.extend(quote! { #[doc = #comment] });
61    }
62
63    output
64}
65
66pub(crate) fn generate_module_doc_comments(content: &str) -> TokenStream {
67    use quote::quote;
68    let lines: Vec<_> = content.trim_end().lines().collect();
69    let mut output = TokenStream::new();
70
71    for line in lines {
72        let comment = format!("//! {}", line);
73        output.extend(quote! { #![doc = #comment] });
74    }
75
76    output
77}
78
79pub(crate) fn is_omnidoc_attr(attr: &syncdoc_core::parse::Attribute) -> bool {
80    use unsynn::ToTokens;
81    let mut ts = TokenStream::new();
82    attr.to_tokens(&mut ts);
83    let s = ts.to_string().replace(' ', "");
84    s.contains("omnidoc") || s.contains("syncdoc::omnidoc")
85}