pub trait Labeller<'a> {
type Node;
type Edge;
type Subgraph;
fn graph_id(&'a self) -> crate::Result<crate::Id<'a>>;
fn node_id(&'a self, n: &Self::Node) -> crate::Result<crate::Id<'a>>;
fn node_shape(&'a self, _node: &Self::Node) -> Option<Text<'a>> {
None
}
fn node_label(&'a self, n: &Self::Node) -> crate::Result<Text<'a>> {
self.node_id(n).map(|x| Text::LabelStr(x.name))
}
fn node_style(&'a self, _n: &Self::Node) -> crate::Style {
crate::Style::None
}
fn node_color(&'a self, _node: &Self::Node) -> Option<Text<'a>> {
None
}
fn edge_end_arrow(&'a self, _e: &Self::Edge) -> crate::Arrow {
crate::Arrow::default()
}
fn edge_start_arrow(&'a self, _e: &Self::Edge) -> crate::Arrow {
crate::Arrow::default()
}
fn edge_label(&'a self, _e: &Self::Edge) -> Text<'a> {
Text::LabelStr("".into())
}
fn edge_style(&'a self, _e: &Self::Edge) -> crate::Style {
crate::Style::None
}
fn edge_color(&'a self, _e: &Self::Edge) -> Option<Text<'a>> {
None
}
fn subgraph_id(&'a self, _s: &Self::Subgraph) -> Option<crate::Id<'a>> {
None
}
fn subgraph_label(&'a self, _s: &Self::Subgraph) -> Text<'a> {
Text::LabelStr("".into())
}
fn subgraph_style(&'a self, _s: &Self::Subgraph) -> crate::Style {
crate::Style::None
}
fn subgraph_shape(&'a self, _s: &Self::Subgraph) -> Option<Text<'a>> {
None
}
fn subgraph_color(&'a self, _s: &Self::Subgraph) -> Option<crate::label::Text<'a>> {
None
}
#[inline]
fn kind(&self) -> crate::Kind {
crate::Kind::Digraph
}
}
pub enum Text<'a> {
LabelStr(std::borrow::Cow<'a, str>),
EscStr(std::borrow::Cow<'a, str>),
HtmlStr(std::borrow::Cow<'a, str>),
}
impl<'a> Text<'a> {
pub fn label<S: Into<std::borrow::Cow<'a, str>>>(s: S) -> Self {
Self::LabelStr(s.into())
}
pub fn html<S: Into<std::borrow::Cow<'a, str>>>(s: S) -> Self {
Self::HtmlStr(s.into())
}
fn escape_char<F>(c: char, mut f: F)
where
F: FnMut(char),
{
match c {
'\\' => f(c),
_ => {
for c in c.escape_default() {
f(c)
}
}
}
}
fn escape_str(s: &str) -> String {
let mut out = String::with_capacity(s.len());
for c in s.chars() {
Self::escape_char(c, |c| out.push(c));
}
out
}
fn pre_escaped_content(self) -> std::borrow::Cow<'a, str> {
match self {
Self::EscStr(s) => s,
Self::LabelStr(s) => {
if s.contains('\\') {
(*s).escape_default().to_string().into()
} else {
s
}
}
Self::HtmlStr(s) => s,
}
}
pub fn suffix_line(self, suffix: Self) -> Self {
let mut prefix = self.pre_escaped_content().into_owned();
let suffix = suffix.pre_escaped_content();
prefix.push_str(r"\n\n");
prefix.push_str(&suffix);
Self::EscStr(prefix.into())
}
}
impl<'a> std::fmt::Display for Text<'a> {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let s = match *self {
Self::LabelStr(ref s) => format!("\"{}\"", s.escape_default()),
Self::EscStr(ref s) => format!("\"{}\"", Self::escape_str(s)),
Self::HtmlStr(ref s) => format!("<{}>", s),
};
write!(f, "{s}")
}
}