use std::collections::BTreeSet;
use std::fmt;
use serde::{Deserialize, Serialize};
use crate::engine::{
DUGONG_LAYOUT_ENGINE_ID, LayoutEngineId, MIND_MAP_FREEFORM_LAYOUT_ENGINE_ID,
MIND_MAP_RADIAL_LAYOUT_ENGINE_ID, TIDY_TREE_LAYOUT_ENGINE_ID,
};
pub const LAYERED_DAG_LAYOUT_FAMILY_ID: &str = "layered_dag";
pub const MIND_MAP_LAYOUT_FAMILY_ID: &str = "mind_map";
#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(transparent)]
pub struct LayoutFamilyId(String);
impl LayoutFamilyId {
pub fn new(id: impl Into<String>) -> Self {
Self(id.into())
}
pub fn layered_dag() -> Self {
Self::new(LAYERED_DAG_LAYOUT_FAMILY_ID)
}
pub fn mind_map() -> Self {
Self::new(MIND_MAP_LAYOUT_FAMILY_ID)
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl fmt::Display for LayoutFamilyId {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
f.write_str(&self.0)
}
}
impl From<&str> for LayoutFamilyId {
fn from(value: &str) -> Self {
Self::new(value)
}
}
impl From<String> for LayoutFamilyId {
fn from(value: String) -> Self {
Self::new(value)
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LayoutFamilyMetadata {
pub id: LayoutFamilyId,
pub name: String,
}
impl LayoutFamilyMetadata {
pub fn new(id: impl Into<LayoutFamilyId>, name: impl Into<String>) -> Self {
Self {
id: id.into(),
name: name.into(),
}
}
pub fn layered_dag() -> Self {
Self::new(LayoutFamilyId::layered_dag(), "Layered DAG")
}
pub fn mind_map() -> Self {
Self::new(LayoutFamilyId::mind_map(), "Mind map")
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum LayoutEngineCapability {
DirectionalLayout,
EdgeRouting,
PinnedNodes,
OverlapAvoidance,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct LayoutEngineMetadata {
pub engine: LayoutEngineId,
pub family: LayoutFamilyId,
pub name: String,
#[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
pub capabilities: BTreeSet<LayoutEngineCapability>,
}
impl LayoutEngineMetadata {
pub fn new(
engine: impl Into<LayoutEngineId>,
family: impl Into<LayoutFamilyId>,
name: impl Into<String>,
) -> Self {
Self {
engine: engine.into(),
family: family.into(),
name: name.into(),
capabilities: BTreeSet::new(),
}
}
pub fn with_capabilities(
mut self,
capabilities: impl IntoIterator<Item = LayoutEngineCapability>,
) -> Self {
self.capabilities.extend(capabilities);
self
}
pub fn dugong() -> Self {
Self::new(
LayoutEngineId::new(DUGONG_LAYOUT_ENGINE_ID),
LayoutFamilyId::layered_dag(),
"Dugong layered DAG",
)
.with_capabilities([
LayoutEngineCapability::DirectionalLayout,
LayoutEngineCapability::EdgeRouting,
])
}
pub fn tidy_tree() -> Self {
Self::new(
LayoutEngineId::new(TIDY_TREE_LAYOUT_ENGINE_ID),
LayoutFamilyId::layered_dag(),
"Tidy tree",
)
.with_capabilities([
LayoutEngineCapability::DirectionalLayout,
LayoutEngineCapability::EdgeRouting,
])
}
pub fn mind_map_radial() -> Self {
Self::new(
LayoutEngineId::new(MIND_MAP_RADIAL_LAYOUT_ENGINE_ID),
LayoutFamilyId::mind_map(),
"Radial mind map",
)
.with_capabilities([
LayoutEngineCapability::DirectionalLayout,
LayoutEngineCapability::EdgeRouting,
LayoutEngineCapability::PinnedNodes,
])
}
pub fn mind_map_freeform() -> Self {
Self::new(
LayoutEngineId::new(MIND_MAP_FREEFORM_LAYOUT_ENGINE_ID),
LayoutFamilyId::mind_map(),
"Freeform mind map",
)
.with_capabilities([
LayoutEngineCapability::DirectionalLayout,
LayoutEngineCapability::EdgeRouting,
LayoutEngineCapability::PinnedNodes,
LayoutEngineCapability::OverlapAvoidance,
])
}
}