jellyflow_layout/
family.rs1use std::collections::BTreeSet;
2use std::fmt;
3
4use serde::{Deserialize, Serialize};
5
6use crate::engine::{
7 DUGONG_LAYOUT_ENGINE_ID, LayoutEngineId, MIND_MAP_FREEFORM_LAYOUT_ENGINE_ID,
8 MIND_MAP_RADIAL_LAYOUT_ENGINE_ID,
9};
10
11pub const LAYERED_DAG_LAYOUT_FAMILY_ID: &str = "layered_dag";
13pub const MIND_MAP_LAYOUT_FAMILY_ID: &str = "mind_map";
15
16#[derive(Debug, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
18#[serde(transparent)]
19pub struct LayoutFamilyId(String);
20
21impl LayoutFamilyId {
22 pub fn new(id: impl Into<String>) -> Self {
24 Self(id.into())
25 }
26
27 pub fn layered_dag() -> Self {
29 Self::new(LAYERED_DAG_LAYOUT_FAMILY_ID)
30 }
31
32 pub fn mind_map() -> Self {
34 Self::new(MIND_MAP_LAYOUT_FAMILY_ID)
35 }
36
37 pub fn as_str(&self) -> &str {
39 &self.0
40 }
41}
42
43impl fmt::Display for LayoutFamilyId {
44 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
45 f.write_str(&self.0)
46 }
47}
48
49impl From<&str> for LayoutFamilyId {
50 fn from(value: &str) -> Self {
51 Self::new(value)
52 }
53}
54
55impl From<String> for LayoutFamilyId {
56 fn from(value: String) -> Self {
57 Self::new(value)
58 }
59}
60
61#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
63pub struct LayoutFamilyMetadata {
64 pub id: LayoutFamilyId,
65 pub name: String,
66}
67
68impl LayoutFamilyMetadata {
69 pub fn new(id: impl Into<LayoutFamilyId>, name: impl Into<String>) -> Self {
71 Self {
72 id: id.into(),
73 name: name.into(),
74 }
75 }
76
77 pub fn layered_dag() -> Self {
79 Self::new(LayoutFamilyId::layered_dag(), "Layered DAG")
80 }
81
82 pub fn mind_map() -> Self {
84 Self::new(LayoutFamilyId::mind_map(), "Mind map")
85 }
86}
87
88#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
90#[serde(rename_all = "snake_case")]
91pub enum LayoutEngineCapability {
92 DirectionalLayout,
94 EdgeRouting,
96 PinnedNodes,
98 OverlapAvoidance,
100}
101
102#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
104pub struct LayoutEngineMetadata {
105 pub engine: LayoutEngineId,
106 pub family: LayoutFamilyId,
107 pub name: String,
108 #[serde(default, skip_serializing_if = "BTreeSet::is_empty")]
109 pub capabilities: BTreeSet<LayoutEngineCapability>,
110}
111
112impl LayoutEngineMetadata {
113 pub fn new(
115 engine: impl Into<LayoutEngineId>,
116 family: impl Into<LayoutFamilyId>,
117 name: impl Into<String>,
118 ) -> Self {
119 Self {
120 engine: engine.into(),
121 family: family.into(),
122 name: name.into(),
123 capabilities: BTreeSet::new(),
124 }
125 }
126
127 pub fn with_capabilities(
129 mut self,
130 capabilities: impl IntoIterator<Item = LayoutEngineCapability>,
131 ) -> Self {
132 self.capabilities.extend(capabilities);
133 self
134 }
135
136 pub fn dugong() -> Self {
138 Self::new(
139 LayoutEngineId::new(DUGONG_LAYOUT_ENGINE_ID),
140 LayoutFamilyId::layered_dag(),
141 "Dugong layered DAG",
142 )
143 .with_capabilities([
144 LayoutEngineCapability::DirectionalLayout,
145 LayoutEngineCapability::EdgeRouting,
146 ])
147 }
148
149 pub fn mind_map_radial() -> Self {
151 Self::new(
152 LayoutEngineId::new(MIND_MAP_RADIAL_LAYOUT_ENGINE_ID),
153 LayoutFamilyId::mind_map(),
154 "Radial mind map",
155 )
156 .with_capabilities([
157 LayoutEngineCapability::DirectionalLayout,
158 LayoutEngineCapability::EdgeRouting,
159 LayoutEngineCapability::PinnedNodes,
160 ])
161 }
162
163 pub fn mind_map_freeform() -> Self {
165 Self::new(
166 LayoutEngineId::new(MIND_MAP_FREEFORM_LAYOUT_ENGINE_ID),
167 LayoutFamilyId::mind_map(),
168 "Freeform mind map",
169 )
170 .with_capabilities([
171 LayoutEngineCapability::DirectionalLayout,
172 LayoutEngineCapability::EdgeRouting,
173 LayoutEngineCapability::PinnedNodes,
174 LayoutEngineCapability::OverlapAvoidance,
175 ])
176 }
177}