use super::sections::is_rust_fence_tag;
mod escaping;
mod prose;
mod references;
mod type_wrappers;
mod utf8;
use escaping::{escape_jsdoc_block_close, xml_escape_for_csharp};
use prose::apply_prose_transforms;
pub(crate) use references::wrap_bare_bracket_references;
#[derive(Copy, Clone, Debug, PartialEq, Eq)]
pub enum DocTarget {
PhpDoc,
JavaDoc,
TsDoc,
JsDoc,
CSharpDoc,
}
pub fn sanitize_rust_idioms(text: &str, target: DocTarget) -> String {
sanitize_rust_idioms_inner(text, target, true)
}
pub fn sanitize_rust_idioms_keep_sections(text: &str, target: DocTarget) -> String {
sanitize_rust_idioms_inner(text, target, false)
}
fn sanitize_rust_idioms_inner(text: &str, target: DocTarget, drop_csharp_sections: bool) -> String {
let mut out = String::with_capacity(text.len());
let mut in_rust_fence = false;
let mut in_other_fence = false;
let mut csharp_section_dropped = false;
for line in text.lines() {
if csharp_section_dropped {
continue;
}
let trimmed = line.trim_start();
if drop_csharp_sections
&& matches!(target, DocTarget::CSharpDoc)
&& !in_rust_fence
&& !in_other_fence
&& is_rustdoc_section_heading(trimmed)
{
csharp_section_dropped = true;
continue;
}
if let Some(rest) = trimmed.strip_prefix("```") {
if in_rust_fence {
in_rust_fence = false;
match target {
DocTarget::TsDoc
| DocTarget::JsDoc
| DocTarget::CSharpDoc
| DocTarget::PhpDoc
| DocTarget::JavaDoc => {
}
}
continue;
}
if in_other_fence {
in_other_fence = false;
out.push_str(line);
out.push('\n');
continue;
}
let lang = rest.split(',').next().unwrap_or("").trim();
let is_rust = is_rust_fence_tag(lang);
if is_rust {
in_rust_fence = true;
match target {
DocTarget::TsDoc
| DocTarget::JsDoc
| DocTarget::CSharpDoc
| DocTarget::PhpDoc
| DocTarget::JavaDoc => {
}
}
continue;
}
in_other_fence = true;
out.push_str(line);
out.push('\n');
continue;
}
if in_rust_fence {
match target {
DocTarget::TsDoc | DocTarget::JsDoc | DocTarget::CSharpDoc | DocTarget::PhpDoc | DocTarget::JavaDoc => {
}
}
continue;
}
if in_other_fence {
out.push_str(line);
out.push('\n');
continue;
}
let stripped_indent = line.trim_start();
if stripped_indent.starts_with("#[") && stripped_indent.ends_with(']') {
continue;
}
let sanitized = apply_prose_transforms(line, target);
out.push_str(&sanitized);
out.push('\n');
}
if out.ends_with('\n') && !text.ends_with('\n') {
out.pop();
}
if matches!(target, DocTarget::TsDoc | DocTarget::JsDoc) {
out = escape_jsdoc_block_close(&out);
}
if matches!(target, DocTarget::CSharpDoc) {
out = xml_escape_for_csharp(&out);
}
out
}
fn is_rustdoc_section_heading(trimmed: &str) -> bool {
let Some(rest) = trimmed.strip_prefix("# ") else {
return false;
};
let head = rest.trim().to_ascii_lowercase();
matches!(
head.as_str(),
"arguments" | "args" | "returns" | "errors" | "panics" | "safety" | "example" | "examples"
)
}