syncdoc_core/
doc_injector.rs1use proc_macro2::TokenStream;
4use quote::quote;
5use unsynn::*;
6
7use crate::parse::{SyncDocArg, SyncDocInner};
8use crate::path_utils::make_manifest_relative_path;
9
10pub fn omnidoc_impl(doc_path: String, cfg_attr: Option<String>, item: TokenStream) -> TokenStream {
12 let call_site = proc_macro2::Span::call_site();
14 let local_file = call_site.local_file().expect("Could not find local file");
15 let rel_doc_path = make_manifest_relative_path(&doc_path, &local_file);
16 if let Some(cfg_value) = cfg_attr {
17 let cfg_ident = proc_macro2::Ident::new(&cfg_value, proc_macro2::Span::call_site());
18 quote! {
19 #[cfg_attr(#cfg_ident, doc = include_str!(#rel_doc_path))]
20 #item
21 }
22 } else {
23 quote! {
24 #[doc = include_str!(#rel_doc_path)]
25 #item
26 }
27 }
28}
29
30pub fn module_doc_impl(args: TokenStream) -> core::result::Result<TokenStream, TokenStream> {
35 let call_site = proc_macro2::Span::call_site();
36 let source_file = call_site
37 .local_file()
38 .ok_or_else(|| {
39 let error = "Could not determine source file location";
40 quote! { compile_error!(#error) }
41 })?
42 .to_string_lossy()
43 .to_string();
44
45 let base_path = if args.is_empty() {
47 crate::config::get_docs_path(&source_file).map_err(|e| {
49 let error = format!("Failed to get docs path from config: {}", e);
50 quote! { compile_error!(#error) }
51 })?
52 } else {
53 let mut args_iter = args.into_token_iter();
55 match parse_syncdoc_args(&mut args_iter) {
56 Ok(parsed_args) => parsed_args.base_path,
57 Err(e) => {
58 let error = format!("Failed to parse module_doc args: {}", e);
59 return Err(quote! { compile_error!(#error) });
60 }
61 }
62 };
63
64 let module_path = crate::path_utils::extract_module_path(&source_file);
66 let doc_path = if module_path.is_empty() {
67 let file_stem = std::path::Path::new(&source_file)
69 .file_stem()
70 .and_then(|s| s.to_str())
71 .unwrap_or("module");
72 format!("{}/{}.md", base_path, file_stem)
73 } else {
74 format!("{}/{}.md", base_path, module_path)
75 };
76
77 let local_file = call_site.local_file().ok_or_else(|| {
79 let error = "Could not find local file";
80 quote! { compile_error!(#error) }
81 })?;
82 let rel_doc_path = make_manifest_relative_path(&doc_path, &local_file);
83
84 Ok(quote! {
86 include_str!(#rel_doc_path)
87 })
88}
89
90#[derive(Debug)]
91struct SyncDocArgs {
92 base_path: String,
93 name: Option<String>,
94 cfg_attr: Option<String>,
95}
96
97fn parse_syncdoc_args(input: &mut TokenIter) -> core::result::Result<SyncDocArgs, String> {
98 match input.parse::<SyncDocInner>() {
99 Ok(parsed) => {
100 let mut args = SyncDocArgs {
101 base_path: String::new(),
102 name: None,
103 cfg_attr: None,
104 };
105
106 if let Some(arg_list) = parsed.args {
107 for arg in arg_list.0 {
108 match arg.value {
109 SyncDocArg::Path(path_arg) => {
110 args.base_path = path_arg.value.as_str().to_string();
111 }
112 SyncDocArg::Name(name_arg) => {
113 args.name = Some(name_arg.value.as_str().to_string());
114 }
115 SyncDocArg::CfgAttr(cfg_attr_arg) => {
116 args.cfg_attr = Some(cfg_attr_arg.value.as_str().to_string());
117 }
118 }
119 }
120 }
121
122 if args.base_path.is_empty() || args.cfg_attr.is_none() {
123 if args.base_path.is_empty() {
125 let call_site = proc_macro2::Span::call_site();
127 let source_file = call_site
128 .local_file()
129 .ok_or("Could not determine source file location")?
130 .to_string_lossy()
131 .to_string();
132
133 let base_path = crate::config::get_docs_path(&source_file)
134 .map_err(|e| format!("Failed to get docs path from config: {}", e))?;
135
136 let module_path = crate::path_utils::extract_module_path(&source_file);
138 args.base_path = if module_path.is_empty() {
139 base_path
140 } else {
141 format!("{}/{}", base_path, module_path)
142 };
143 }
144
145 if args.cfg_attr.is_none() {
147 let call_site = proc_macro2::Span::call_site();
148 if let Some(source_path) = call_site.local_file() {
149 let source_file = source_path.to_string_lossy().to_string();
150 if let Ok(cfg) = crate::config::get_cfg_attr(&source_file) {
151 args.cfg_attr = cfg;
152 }
153 }
154 }
155 }
156
157 Ok(args)
158 }
159 Err(e) => Err(format!("Failed to parse syncdoc args: {}", e)),
160 }
161}