// Copyright 2025 International Digital Economy Academy
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
///|
/// Arrowhead types for diago diagram edges
///
/// This enum represents all supported arrowhead types in diago diagrams.
/// Each arrowhead has its own marker rendering for SVG output.
///|
/// All supported arrowhead types
pub(all) enum ArrowheadType {
/// No arrowhead
None
/// Default filled triangle arrow
Triangle
/// Filled arrow with a concave tail
Arrow
/// Unfilled diamond shape
Diamond
/// Filled diamond shape
FilledDiamond
/// Unfilled circle
Circle
/// Filled circle
FilledCircle
/// Open (unfilled) triangle arrow
Open
/// Simple line terminator
Line
/// Crow's foot: exactly one (vertical line)
CfOne
/// Crow's foot: zero or more (crow's foot)
CfMany
/// Crow's foot: exactly one, required (line with perpendicular)
CfOneRequired
/// Crow's foot: one or more, required (crow's foot with line)
CfManyRequired
/// Crow's foot: zero or one, optional (line with circle)
CfOneOptional
/// Crow's foot: zero or more, optional (crow's foot with circle)
CfManyOptional
} derive(Eq, Debug)
///|
pub impl Show for ArrowheadType with fn output(self, logger) {
logger.write_string(self.to_arrowhead_string())
}
///|
/// Parse an arrowhead type from a string
pub fn ArrowheadType::from_string(s : String) -> ArrowheadType {
match s.to_lower() {
"none" | "" => None
"triangle" => Triangle
"arrow" => Arrow
"diamond" => Diamond
"filled-diamond" => FilledDiamond
"circle" => Circle
"filled-circle" => FilledCircle
"open" => Open
"line" => Line
"cf-one" | "cfone" => CfOne
"cf-many" | "cfmany" => CfMany
"cf-one-required" | "cfonerequired" => CfOneRequired
"cf-many-required" | "cfmanyrequired" => CfManyRequired
"cf-one-optional" | "cfoneoptional" => CfOneOptional
"cf-many-optional" | "cfmanyoptional" => CfManyOptional
_ => Triangle // Default to triangle for unknown types
}
}
///|
/// Convert arrowhead type to canonical string representation
pub fn ArrowheadType::to_arrowhead_string(self : ArrowheadType) -> String {
match self {
None => "none"
Triangle => "triangle"
Arrow => "arrow"
Diamond => "diamond"
FilledDiamond => "filled-diamond"
Circle => "circle"
FilledCircle => "filled-circle"
Open => "open"
Line => "line"
CfOne => "cf-one"
CfMany => "cf-many"
CfOneRequired => "cf-one-required"
CfManyRequired => "cf-many-required"
CfOneOptional => "cf-one-optional"
CfManyOptional => "cf-many-optional"
}
}
///|
/// Get the SVG marker ID for this arrowhead type
pub fn ArrowheadType::marker_id(self : ArrowheadType, reverse : Bool) -> String {
let base = match self {
None => return "" // No marker needed
Triangle => "arrow-triangle"
Arrow => "arrow"
Diamond => "arrow-diamond"
FilledDiamond => "arrow-diamond-filled"
Circle => "arrow-circle"
FilledCircle => "arrow-circle-filled"
Open => "arrow-open"
Line => "arrow-line"
CfOne => "arrow-cf-one"
CfMany => "arrow-cf-many"
CfOneRequired => "arrow-cf-one-required"
CfManyRequired => "arrow-cf-many-required"
CfOneOptional => "arrow-cf-one-optional"
CfManyOptional => "arrow-cf-many-optional"
}
if reverse {
base + "-reverse"
} else {
base
}
}
///|
/// Get the marker URL for SVG use
pub fn ArrowheadType::marker_url(
self : ArrowheadType,
reverse : Bool,
) -> String {
let id = self.marker_id(reverse)
if id.is_empty() {
""
} else {
"url(#" + id + ")"
}
}
///|
/// Check if this is a crow's foot notation type
pub fn ArrowheadType::is_crows_foot(self : ArrowheadType) -> Bool {
match self {
CfOne
| CfMany
| CfOneRequired
| CfManyRequired
| CfOneOptional
| CfManyOptional => true
_ => false
}
}
///|
/// Get ASCII representation of this arrowhead
pub fn ArrowheadType::to_ascii(self : ArrowheadType, reverse : Bool) -> String {
match self {
None => if reverse { "-" } else { "-" }
Triangle => if reverse { "<" } else { ">" }
Arrow => if reverse { "<" } else { ">" }
Diamond | FilledDiamond => if reverse { "<>" } else { "<>" }
Circle | FilledCircle => if reverse { "o" } else { "o" }
Open => if reverse { "<" } else { ">" }
Line => if reverse { "|" } else { "|" }
CfOne => if reverse { "|" } else { "|" }
CfMany => if reverse { "<" } else { ">" }
CfOneRequired => if reverse { "||" } else { "||" }
CfManyRequired => if reverse { "<|" } else { "|>" }
CfOneOptional => if reverse { "o|" } else { "|o" }
CfManyOptional => if reverse { "o<" } else { ">o" }
}
}