pub mod dot;
#[cfg(feature = "json")]
pub mod json;
pub mod mermaid;
#[cfg(feature = "xml")]
pub mod xml;
fn escape_xml_attr(s: &str) -> std::borrow::Cow<'_, str> {
let first = s.find(|ch: char| {
matches!(ch, '&' | '<' | '>' | '"' | '\'')
|| (ch.is_control() && ch != '\t' && ch != '\n' && ch != '\r')
});
let Some(pos) = first else {
return std::borrow::Cow::Borrowed(s);
};
let mut out = String::with_capacity(s.len() + 8);
out.push_str(&s[..pos]);
for ch in s[pos..].chars() {
match ch {
'&' => out.push_str("&"),
'<' => out.push_str("<"),
'>' => out.push_str(">"),
'"' => out.push_str("""),
'\'' => out.push_str("'"),
c if c.is_control() && c != '\t' && c != '\n' && c != '\r' => {}
_ => out.push(ch),
}
}
std::borrow::Cow::Owned(out)
}
fn escape_dot(s: &str) -> std::borrow::Cow<'_, str> {
let first = s.find(|ch: char| {
matches!(ch, '"' | '\\') || (ch.is_control() && ch != '\t' && ch != '\n' && ch != '\r')
});
let Some(pos) = first else {
return std::borrow::Cow::Borrowed(s);
};
let mut out = String::with_capacity(s.len() + 8);
out.push_str(&s[..pos]);
for ch in s[pos..].chars() {
match ch {
'"' => out.push_str("\\\""),
'\\' => out.push_str("\\\\"),
c if c.is_control() && c != '\t' && c != '\n' && c != '\r' => {}
_ => out.push(ch),
}
}
std::borrow::Cow::Owned(out)
}
fn escape_mermaid(s: &str) -> std::borrow::Cow<'_, str> {
let first = s.find(['\n', '\r', '{', '}', '"', '|', ';', '%']);
let Some(pos) = first else {
return std::borrow::Cow::Borrowed(s);
};
let mut out = String::with_capacity(s.len() + 8);
out.push_str(&s[..pos]);
for ch in s[pos..].chars() {
match ch {
'\n' | '\r' => out.push(' '),
'{' | '}' | '"' | '|' | ';' | '%' => out.push('_'),
_ => out.push(ch),
}
}
std::borrow::Cow::Owned(out)
}
pub struct IoAdapter<W> {
inner: W,
error: Option<std::io::Error>,
}
impl<W: std::io::Write> IoAdapter<W> {
pub fn new(inner: W) -> Self {
Self { inner, error: None }
}
pub fn into_io_result(self) -> std::io::Result<()> {
match self.error {
Some(e) => Err(e),
None => Ok(()),
}
}
}
impl<W: std::io::Write> std::fmt::Write for IoAdapter<W> {
fn write_str(&mut self, s: &str) -> std::fmt::Result {
self.inner.write_all(s.as_bytes()).map_err(|e| {
self.error = Some(e);
std::fmt::Error
})
}
}
const MAX_CACHED_DEPTH: usize = 20;
struct IndentCache {
indents: [String; MAX_CACHED_DEPTH + 1],
}
impl IndentCache {
fn new() -> Self {
let indents = std::array::from_fn(|i| " ".repeat(i));
Self { indents }
}
fn get(&self, depth: usize) -> &str {
if depth <= MAX_CACHED_DEPTH {
&self.indents[depth]
} else {
&self.indents[MAX_CACHED_DEPTH]
}
}
}