#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Attribute {
pub type_name: String,
pub name: String,
pub keys: Vec<AttributeKey>,
pub comment: Option<String>,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum AttributeKey {
PrimaryKey,
ForeignKey,
UniqueKey,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Entity {
pub name: String,
pub attributes: Vec<Attribute>,
}
impl Entity {
pub fn bare(name: impl Into<String>) -> Self {
Self {
name: name.into(),
attributes: Vec::new(),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum Cardinality {
ExactlyOne,
ZeroOrOne,
OneOrMany,
ZeroOrMany,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub enum LineStyle {
Identifying,
NonIdentifying,
}
impl LineStyle {
pub fn is_dashed(self) -> bool {
matches!(self, LineStyle::NonIdentifying)
}
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Relationship {
pub from: String,
pub to: String,
pub from_cardinality: Cardinality,
pub to_cardinality: Cardinality,
pub line_style: LineStyle,
pub label: Option<String>,
}
#[derive(Debug, Clone, PartialEq, Eq, Default)]
pub struct ErDiagram {
pub entities: Vec<Entity>,
pub relationships: Vec<Relationship>,
}
impl ErDiagram {
pub fn entity_index(&self, name: &str) -> Option<usize> {
self.entities.iter().position(|e| e.name == name)
}
pub fn ensure_entity(&mut self, name: &str) -> usize {
if let Some(idx) = self.entity_index(name) {
return idx;
}
self.entities.push(Entity::bare(name));
self.entities.len() - 1
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn line_style_is_dashed_distinguishes_identifying_from_non() {
assert!(LineStyle::NonIdentifying.is_dashed());
assert!(!LineStyle::Identifying.is_dashed());
}
#[test]
fn entity_bare_starts_with_no_attributes() {
let e = Entity::bare("CUSTOMER");
assert_eq!(e.name, "CUSTOMER");
assert!(e.attributes.is_empty());
}
#[test]
fn ensure_entity_inserts_then_reuses() {
let mut diag = ErDiagram::default();
let first = diag.ensure_entity("A");
let second = diag.ensure_entity("A");
let third = diag.ensure_entity("B");
assert_eq!(first, 0);
assert_eq!(second, 0); assert_eq!(third, 1);
assert_eq!(diag.entities.len(), 2);
}
#[test]
fn entity_index_returns_none_for_unknown() {
let diag = ErDiagram::default();
assert_eq!(diag.entity_index("X"), None);
}
}