use crate::common::outline::{Decoration, OutlineDb};
use crate::detect::DiagramType;
use crate::errors::{Error, ErrorCode};
#[derive(Clone, Debug, PartialEq)]
pub(crate) struct MindmapNode {
pub(crate) id: usize,
pub(crate) node_id: String,
pub(crate) level: usize,
pub(crate) descr: String,
pub(crate) kind: usize,
pub(crate) children: Vec<usize>,
pub(crate) is_root: bool,
pub(crate) section: Option<usize>,
pub(crate) class: Option<String>,
pub(crate) icon: Option<String>,
}
const SECTIONS: usize = 11;
#[derive(Clone, Debug, Default)]
pub(crate) struct MindmapDb {
pub(crate) title: Option<String>,
pub(crate) nodes: Vec<MindmapNode>,
base_level: usize,
open: Vec<usize>,
}
impl OutlineDb for MindmapDb {
fn add_node(
&mut self,
level: usize,
id: &str,
descr: &str,
kind: usize,
_shape_data: Option<&str>,
) -> Result<(), Error> {
let is_root = self.nodes.is_empty();
if is_root {
self.base_level = level;
}
let level = level.checked_sub(self.base_level);
let mut depth = self.open.len();
while depth > 0 && level.is_none_or(|l| self.nodes[self.open[depth - 1]].level >= l) {
depth -= 1;
}
let parent = depth.checked_sub(1).map(|d| self.open[d]);
if parent.is_none() && !is_root {
let mut e = Error::new(
format!("There can be only one root. No parent could be found for (\"{descr}\")"),
ErrorCode::Semantic,
);
e.diagram = Some(DiagramType::Mindmap);
return Err(e);
}
let id_at = self.nodes.len();
let mut node = MindmapNode {
id: id_at,
node_id: id.to_string(),
level: level.unwrap_or(0),
descr: descr.to_string(),
kind,
children: Vec::new(),
is_root,
section: None,
class: None,
icon: None,
};
if let Some(p) = parent {
let parent = &mut self.nodes[p];
node.section = if parent.is_root {
Some(parent.children.len() % SECTIONS)
} else {
parent.section
};
parent.children.push(id_at);
}
self.open.truncate(depth);
self.open.push(id_at);
self.nodes.push(node);
Ok(())
}
fn decorate_node(&mut self, decoration: Decoration) {
let Some(node) = self.nodes.last_mut() else {
return;
};
match decoration {
Decoration::Icon(icon) if !icon.is_empty() => node.icon = Some(icon.to_string()),
Decoration::Class(class) if !class.is_empty() => node.class = Some(class.to_string()),
_ => {}
}
}
}