use proc_macro2::TokenStream;
use quote::quote;
use unsynn::*;
use crate::parse::{SyncDocArg, SyncDocInner};
use crate::path_utils::make_manifest_relative_path;
pub fn omnidoc_impl(doc_path: String, cfg_attr: Option<String>, item: TokenStream) -> TokenStream {
let call_site = proc_macro2::Span::call_site();
let local_file = call_site.local_file().expect("Could not find local file");
let rel_doc_path = make_manifest_relative_path(&doc_path, &local_file);
if let Some(cfg_value) = cfg_attr {
let cfg_ident = proc_macro2::Ident::new(&cfg_value, proc_macro2::Span::call_site());
quote! {
#[cfg_attr(#cfg_ident, doc = include_str!(#rel_doc_path))]
#item
}
} else {
quote! {
#[doc = include_str!(#rel_doc_path)]
#item
}
}
}
pub fn module_doc_impl(args: TokenStream) -> core::result::Result<TokenStream, TokenStream> {
let call_site = proc_macro2::Span::call_site();
let source_file = call_site
.local_file()
.ok_or_else(|| {
let error = "Could not determine source file location";
quote! { compile_error!(#error) }
})?
.to_string_lossy()
.to_string();
let base_path = if args.is_empty() {
crate::config::get_docs_path(&source_file).map_err(|e| {
let error = format!("Failed to get docs path from config: {}", e);
quote! { compile_error!(#error) }
})?
} else {
let mut args_iter = args.into_token_iter();
match parse_syncdoc_args(&mut args_iter) {
Ok(parsed_args) => parsed_args.base_path,
Err(e) => {
let error = format!("Failed to parse module_doc args: {}", e);
return Err(quote! { compile_error!(#error) });
}
}
};
let module_path = crate::path_utils::extract_module_path(&source_file);
let doc_path = if module_path.is_empty() {
let file_stem = std::path::Path::new(&source_file)
.file_stem()
.and_then(|s| s.to_str())
.unwrap_or("module");
format!("{}/{}.md", base_path, file_stem)
} else {
format!("{}/{}.md", base_path, module_path)
};
let local_file = call_site.local_file().ok_or_else(|| {
let error = "Could not find local file";
quote! { compile_error!(#error) }
})?;
let rel_doc_path = make_manifest_relative_path(&doc_path, &local_file);
Ok(quote! {
include_str!(#rel_doc_path)
})
}
#[derive(Debug)]
struct SyncDocArgs {
base_path: String,
name: Option<String>,
cfg_attr: Option<String>,
}
fn parse_syncdoc_args(input: &mut TokenIter) -> core::result::Result<SyncDocArgs, String> {
match input.parse::<SyncDocInner>() {
Ok(parsed) => {
let mut args = SyncDocArgs {
base_path: String::new(),
name: None,
cfg_attr: None,
};
if let Some(arg_list) = parsed.args {
for arg in arg_list.0 {
match arg.value {
SyncDocArg::Path(path_arg) => {
args.base_path = path_arg.value.as_str().to_string();
}
SyncDocArg::Name(name_arg) => {
args.name = Some(name_arg.value.as_str().to_string());
}
SyncDocArg::CfgAttr(cfg_attr_arg) => {
args.cfg_attr = Some(cfg_attr_arg.value.as_str().to_string());
}
}
}
}
if args.base_path.is_empty() || args.cfg_attr.is_none() {
if args.base_path.is_empty() {
let call_site = proc_macro2::Span::call_site();
let source_file = call_site
.local_file()
.ok_or("Could not determine source file location")?
.to_string_lossy()
.to_string();
let base_path = crate::config::get_docs_path(&source_file)
.map_err(|e| format!("Failed to get docs path from config: {}", e))?;
let module_path = crate::path_utils::extract_module_path(&source_file);
args.base_path = if module_path.is_empty() {
base_path
} else {
format!("{}/{}", base_path, module_path)
};
}
if args.cfg_attr.is_none() {
let call_site = proc_macro2::Span::call_site();
if let Some(source_path) = call_site.local_file() {
let source_file = source_path.to_string_lossy().to_string();
if let Ok(cfg) = crate::config::get_cfg_attr(&source_file) {
args.cfg_attr = cfg;
}
}
}
}
Ok(args)
}
Err(e) => Err(format!("Failed to parse syncdoc args: {}", e)),
}
}