use std::{fmt, rc::Rc, str::FromStr};
use crate::{draw, identifier::Id, semantic::diagram::Block};
#[derive(Debug, Clone)]
pub struct Node {
id: Id,
name: String,
display_name: Option<String>,
block: Block,
shape_definition: Rc<Box<dyn draw::ShapeDefinition>>,
}
impl Node {
pub fn new(
id: Id,
name: String,
display_name: Option<String>,
block: Block,
shape_definition: Rc<Box<dyn draw::ShapeDefinition>>,
) -> Self {
Self {
id,
name,
display_name,
block,
shape_definition,
}
}
pub fn id(&self) -> Id {
self.id
}
pub fn block(&self) -> &Block {
&self.block
}
pub fn shape_definition(&self) -> &Rc<Box<dyn draw::ShapeDefinition>> {
&self.shape_definition
}
pub fn display_text(&self) -> &str {
self.display_name.as_deref().unwrap_or(&self.name)
}
}
impl fmt::Display for Node {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
write!(f, "{}", self.id)
}
}
#[derive(Debug, Clone)]
pub struct Relation {
source: Id,
target: Id,
arrow_direction: draw::ArrowDirection,
label: Option<String>,
arrow_definition: Rc<draw::ArrowDefinition>,
}
impl Relation {
pub fn new(
source: Id,
target: Id,
arrow_direction: draw::ArrowDirection,
label: Option<String>,
arrow_definition: Rc<draw::ArrowDefinition>,
) -> Self {
Self {
source,
target,
arrow_direction,
label,
arrow_definition,
}
}
pub fn text(&self) -> Option<draw::Text<'_>> {
let label = self.label.as_ref()?;
let text_def = self.arrow_definition.text();
Some(draw::Text::new(text_def, label))
}
pub fn arrow_definition(&self) -> &Rc<draw::ArrowDefinition> {
&self.arrow_definition
}
pub fn source(&self) -> Id {
self.source
}
pub fn target(&self) -> Id {
self.target
}
pub fn arrow_direction(&self) -> draw::ArrowDirection {
self.arrow_direction
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum NoteAlign {
Over,
Left,
Right,
Top,
Bottom,
}
impl FromStr for NoteAlign {
type Err = &'static str;
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"over" => Ok(NoteAlign::Over),
"left" => Ok(NoteAlign::Left),
"right" => Ok(NoteAlign::Right),
"top" => Ok(NoteAlign::Top),
"bottom" => Ok(NoteAlign::Bottom),
_ => Err("Invalid alignment value"),
}
}
}
#[derive(Debug, Clone)]
pub struct Note {
on: Vec<Id>,
align: NoteAlign,
content: String,
definition: Rc<draw::NoteDefinition>,
}
impl Note {
pub fn new(
on: Vec<Id>,
align: NoteAlign,
content: String,
definition: Rc<draw::NoteDefinition>,
) -> Self {
Self {
on,
align,
content,
definition,
}
}
pub fn on(&self) -> &[Id] {
&self.on
}
pub fn align(&self) -> NoteAlign {
self.align
}
pub fn content(&self) -> &str {
&self.content
}
pub fn definition(&self) -> &Rc<draw::NoteDefinition> {
&self.definition
}
}
#[derive(Debug, Clone)]
pub struct Activate {
component: Id,
definition: Rc<draw::ActivationBoxDefinition>,
}
impl Activate {
pub fn new(component: Id, definition: Rc<draw::ActivationBoxDefinition>) -> Self {
Self {
component,
definition,
}
}
pub fn component(&self) -> Id {
self.component
}
pub fn definition(&self) -> &Rc<draw::ActivationBoxDefinition> {
&self.definition
}
}
#[derive(Debug, Clone)]
pub struct Fragment {
operation: String,
sections: Vec<FragmentSection>,
definition: Rc<draw::FragmentDefinition>,
}
impl Fragment {
pub fn new(
operation: String,
sections: Vec<FragmentSection>,
definition: Rc<draw::FragmentDefinition>,
) -> Self {
Self {
operation,
sections,
definition,
}
}
pub fn operation(&self) -> &str {
&self.operation
}
pub fn sections(&self) -> &[FragmentSection] {
&self.sections
}
pub fn definition(&self) -> &Rc<draw::FragmentDefinition> {
&self.definition
}
}
#[derive(Debug, Clone)]
pub struct FragmentSection {
title: Option<String>,
elements: Vec<Element>,
}
impl FragmentSection {
pub fn new(title: Option<String>, elements: Vec<Element>) -> Self {
Self { title, elements }
}
pub fn title(&self) -> Option<&str> {
self.title.as_deref()
}
pub fn elements(&self) -> &[Element] {
&self.elements
}
}
#[derive(Debug, Clone)]
pub enum Element {
Node(Node),
Relation(Relation),
Activate(Activate),
Deactivate(Id),
Fragment(Fragment),
Note(Note),
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_note_align_from_str() {
assert_eq!("over".parse::<NoteAlign>().unwrap(), NoteAlign::Over);
assert_eq!("left".parse::<NoteAlign>().unwrap(), NoteAlign::Left);
assert_eq!("right".parse::<NoteAlign>().unwrap(), NoteAlign::Right);
assert_eq!("top".parse::<NoteAlign>().unwrap(), NoteAlign::Top);
assert_eq!("bottom".parse::<NoteAlign>().unwrap(), NoteAlign::Bottom);
let result: Result<NoteAlign, _> = "invalid".parse();
assert!(result.is_err());
assert_eq!(result.unwrap_err(), "Invalid alignment value");
}
}