use std::fmt;
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct DataModelStructure {
pub data_model: String,
pub relations: Vec<Relation>,
}
#[derive(Debug, Clone, PartialEq, Eq)]
pub struct Relation {
pub source: String,
pub target: String,
pub kind: RelationKind,
pub field: Option<String>,
pub target_data_model: Option<String>,
pub is_builtin: bool,
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
pub enum RelationKind {
Link,
Inherits,
}
impl fmt::Display for RelationKind {
fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
match self {
RelationKind::Link => f.write_str("link"),
RelationKind::Inherits => f.write_str("inherits"),
}
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn data_model_structure_full_construction_and_equality() {
let structure = DataModelStructure {
data_model: "beol".into(),
relations: vec![
Relation {
source: "letter".into(),
target: "Book".into(),
kind: RelationKind::Link,
field: Some("cites".into()),
target_data_model: Some("biblio".into()),
is_builtin: false,
},
Relation {
source: "letter".into(),
target: "writtenSource".into(),
kind: RelationKind::Inherits,
field: None,
target_data_model: None,
is_builtin: false,
},
],
};
let cloned = structure.clone();
assert_eq!(structure, cloned);
assert_eq!(structure.data_model, "beol");
assert_eq!(structure.relations.len(), 2);
}
#[test]
fn data_model_structure_empty_relations() {
let structure = DataModelStructure {
data_model: "minimal".into(),
relations: vec![],
};
let cloned = structure.clone();
assert_eq!(structure, cloned);
assert_eq!(structure.data_model, "minimal");
assert!(structure.relations.is_empty());
}
#[test]
fn relation_link_full_construction_and_equality() {
let rel = Relation {
source: "letter".into(),
target: "Book".into(),
kind: RelationKind::Link,
field: Some("cites".into()),
target_data_model: Some("biblio".into()),
is_builtin: false,
};
let cloned = rel.clone();
assert_eq!(rel, cloned);
assert_eq!(rel.source, "letter");
assert_eq!(rel.target, "Book");
assert_eq!(rel.kind, RelationKind::Link);
assert_eq!(rel.field.as_deref(), Some("cites"));
assert_eq!(rel.target_data_model.as_deref(), Some("biblio"));
assert!(!rel.is_builtin);
}
#[test]
fn relation_inherits_construction_and_equality() {
let rel = Relation {
source: "letter".into(),
target: "writtenSource".into(),
kind: RelationKind::Inherits,
field: None,
target_data_model: None,
is_builtin: false,
};
let cloned = rel.clone();
assert_eq!(rel, cloned);
assert_eq!(rel.source, "letter");
assert_eq!(rel.target, "writtenSource");
assert_eq!(rel.kind, RelationKind::Inherits);
assert!(rel.field.is_none());
assert!(rel.target_data_model.is_none());
assert!(!rel.is_builtin);
}
#[test]
fn relation_kind_display_link() {
assert_eq!(RelationKind::Link.to_string(), "link");
}
#[test]
fn relation_kind_display_inherits() {
assert_eq!(RelationKind::Inherits.to_string(), "inherits");
}
#[test]
fn relation_kind_ordering_link_before_inherits() {
assert!(
RelationKind::Link < RelationKind::Inherits,
"Link must sort before Inherits (D6: link before inherits for the same source)"
);
}
#[test]
fn relation_kind_ordering_inherits_not_less_than_link() {
assert!(RelationKind::Inherits >= RelationKind::Link);
}
#[test]
fn link_relation_has_field_some() {
let rel = Relation {
source: "letter".into(),
target: "person".into(),
kind: RelationKind::Link,
field: Some("hasSender".into()),
target_data_model: None,
is_builtin: false,
};
assert_eq!(rel.kind, RelationKind::Link);
assert!(
rel.field.is_some(),
"a Link relation must have field == Some(...)"
);
}
#[test]
fn non_link_relation_has_field_none() {
let rel = Relation {
source: "letter".into(),
target: "writtenSource".into(),
kind: RelationKind::Inherits,
field: None,
target_data_model: None,
is_builtin: false,
};
assert_ne!(rel.kind, RelationKind::Link);
assert!(
rel.field.is_none(),
"a non-Link relation must have field == None"
);
}
#[test]
fn project_link_field_to_builtin_target_is_not_builtin() {
let rel = Relation {
source: "letter".into(),
target: "Resource".into(),
kind: RelationKind::Link,
field: Some("hasRelation".into()),
target_data_model: None, is_builtin: false, };
assert_eq!(rel.kind, RelationKind::Link);
assert!(
!rel.is_builtin,
"project link field to built-in target is is_builtin=false"
);
assert!(
rel.target_data_model.is_none(),
"system target → target_data_model must be None"
);
}
#[test]
fn inherits_relation_to_system_super_is_builtin() {
let rel = Relation {
source: "letter".into(),
target: "Resource".into(),
kind: RelationKind::Inherits,
field: None,
target_data_model: None, is_builtin: true, };
assert_eq!(rel.kind, RelationKind::Inherits);
assert!(
rel.is_builtin,
"inherits relation to system superclass is is_builtin=true"
);
assert!(
rel.target_data_model.is_none(),
"system target → target_data_model must be None"
);
}
#[test]
fn non_system_target_has_target_data_model_some() {
let in_model_link = Relation {
source: "letter".into(),
target: "person".into(),
kind: RelationKind::Link,
field: Some("hasSender".into()),
target_data_model: Some("beol".into()), is_builtin: false,
};
assert!(
in_model_link.target_data_model.is_some(),
"in-model link target must have target_data_model == Some(...)"
);
assert_eq!(
in_model_link.target_data_model.as_deref(),
Some("beol"),
"in-model target_data_model must equal the baseline DM name"
);
let cross_model_link = Relation {
source: "letter".into(),
target: "Book".into(),
kind: RelationKind::Link,
field: Some("cites".into()),
target_data_model: Some("biblio".into()), is_builtin: false,
};
assert!(
cross_model_link.target_data_model.is_some(),
"cross-model link target must have target_data_model == Some(...)"
);
assert_eq!(
cross_model_link.target_data_model.as_deref(),
Some("biblio"),
"cross-model target_data_model must equal the sibling DM name"
);
assert_ne!(
cross_model_link.target_data_model, None,
"non-system target must NOT have target_data_model == None"
);
}
}