use std::path::{Path, PathBuf};
use crate::{GetterCollection, IdentificationMode, Scope, TokenStreamGetterCollector};
#[derive(Debug)]
pub struct DocCodeGetterCollector<P: TokenStreamGetterCollector> {
code: String,
state: State,
identification_mode: IdentificationMode,
getter_collection: P::GetterCollection,
path: PathBuf,
}
impl<P: TokenStreamGetterCollector> DocCodeGetterCollector<P> {
pub fn new(
path: &Path,
identification_mode: IdentificationMode,
getter_collection: &P::GetterCollection,
) -> Self {
let mut getter_collection = P::GetterCollection::clone(getter_collection);
getter_collection.disable_doc_alias();
DocCodeGetterCollector {
code: String::with_capacity(512),
state: State::None,
identification_mode,
getter_collection,
path: path.to_owned(),
}
}
pub fn have_attribute(&mut self, node: &syn::Attribute) {
if let Some((_, cursor)) = syn::buffer::TokenBuffer::new2(node.tokens.clone())
.begin()
.punct()
{
if let Some((literal, _)) = cursor.literal() {
self.process(
&literal.to_string().trim_matches('"').trim(),
literal.span().start().line,
);
}
}
}
fn process(&mut self, doc_line: &str, offset: usize) {
if doc_line.starts_with("```") {
if !self.state.is_code_block() {
self.getter_collection.set_offset(offset);
if doc_line.len() == 3 || doc_line.ends_with("rust") {
self.state = State::RustCodeBlock;
} else {
self.state = State::CodeBlock;
};
} else {
if self.state.is_rust() {
self.collect();
}
self.state = State::None;
}
} else if self.state.is_rust() && !doc_line.starts_with('#') {
self.code.push_str(&doc_line.replace('\\', &""));
self.code.push('\n');
}
}
fn collect(&mut self) {
match syn::parse_str::<proc_macro2::TokenStream>(&self.code) {
Ok(syntax_tree) => P::collect(
&self.path,
&Scope::Documentation,
&syntax_tree,
self.identification_mode,
&self.getter_collection,
),
Err(_err) => {
#[cfg(feature = "log")]
log::warn!(
"{:?} doc @ {}: {:?}",
self.path,
self.getter_collection.offset(),
_err
);
}
}
self.code.clear();
}
}
#[derive(Debug)]
enum State {
None,
CodeBlock,
RustCodeBlock,
}
impl State {
fn is_code_block(&self) -> bool {
matches!(self, State::RustCodeBlock | State::CodeBlock)
}
fn is_rust(&self) -> bool {
matches!(self, State::RustCodeBlock)
}
}