use indexmap::IndexMap;
use std::sync::Arc;
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
pub struct ElementId(pub Arc<str>);
impl ElementId {
pub fn new(id: impl Into<Arc<str>>) -> Self {
Self(id.into())
}
pub fn generate() -> Self {
use std::time::{SystemTime, UNIX_EPOCH};
let nanos = SystemTime::now()
.duration_since(UNIX_EPOCH)
.map(|d| d.as_nanos())
.unwrap_or(0);
Self(format!("{:032x}", nanos).into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
impl std::fmt::Display for ElementId {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.0)
}
}
impl From<&str> for ElementId {
fn from(s: &str) -> Self {
Self::new(s)
}
}
impl From<String> for ElementId {
fn from(s: String) -> Self {
Self::new(s)
}
}
#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)]
pub enum ElementKind {
Namespace,
Package,
LibraryPackage,
Class,
DataType,
Structure,
Association,
AssociationStructure,
Interaction,
Behavior,
Function,
Predicate,
PartDefinition,
ItemDefinition,
ActionDefinition,
PortDefinition,
AttributeDefinition,
ConnectionDefinition,
InterfaceDefinition,
AllocationDefinition,
RequirementDefinition,
ConstraintDefinition,
StateDefinition,
CalculationDefinition,
UseCaseDefinition,
AnalysisCaseDefinition,
ConcernDefinition,
ViewDefinition,
ViewpointDefinition,
RenderingDefinition,
EnumerationDefinition,
MetadataDefinition,
PartUsage,
ItemUsage,
ActionUsage,
PortUsage,
AttributeUsage,
ConnectionUsage,
InterfaceUsage,
AllocationUsage,
RequirementUsage,
ConstraintUsage,
StateUsage,
TransitionUsage,
CalculationUsage,
ReferenceUsage,
OccurrenceUsage,
FlowConnectionUsage,
SuccessionFlowConnectionUsage,
Feature,
Step,
Expression,
BooleanExpression,
Invariant,
Connector,
BindingConnector,
Succession,
Flow,
MultiplicityRange,
LiteralInteger,
LiteralReal,
LiteralInfinity,
LiteralBoolean,
LiteralString,
NullExpression,
FeatureReferenceExpression,
OperatorExpression,
InvocationExpression,
FeatureChainExpression,
ConstructorExpression,
Membership,
OwningMembership,
FeatureMembership,
ReturnParameterMembership,
ParameterMembership,
EndFeatureMembership,
ResultExpressionMembership,
Import,
NamespaceImport,
MembershipImport,
Specialization,
FeatureTyping,
Subsetting,
ReferenceSubsetting,
CrossSubsetting,
Redefinition,
Conjugation,
FeatureValue,
FeatureChaining,
FeatureInverting,
Intersecting,
Disjoining,
Unioning,
Dependency,
Satisfaction,
Verification,
Comment,
Documentation,
TextualRepresentation,
Alias,
MetadataUsage,
AnnotatingElement,
Annotation,
Classifier,
Metaclass,
Other,
}
impl ElementKind {
pub fn is_definition(&self) -> bool {
matches!(
self,
Self::Package
| Self::LibraryPackage
| Self::Class
| Self::DataType
| Self::Structure
| Self::Association
| Self::AssociationStructure
| Self::Interaction
| Self::Behavior
| Self::Function
| Self::Predicate
| Self::PartDefinition
| Self::ItemDefinition
| Self::ActionDefinition
| Self::PortDefinition
| Self::AttributeDefinition
| Self::ConnectionDefinition
| Self::InterfaceDefinition
| Self::AllocationDefinition
| Self::RequirementDefinition
| Self::ConstraintDefinition
| Self::StateDefinition
| Self::CalculationDefinition
| Self::UseCaseDefinition
| Self::AnalysisCaseDefinition
| Self::ConcernDefinition
| Self::ViewDefinition
| Self::ViewpointDefinition
| Self::RenderingDefinition
| Self::EnumerationDefinition
| Self::MetadataDefinition
)
}
pub fn is_usage(&self) -> bool {
matches!(
self,
Self::PartUsage
| Self::ItemUsage
| Self::ActionUsage
| Self::PortUsage
| Self::AttributeUsage
| Self::ConnectionUsage
| Self::InterfaceUsage
| Self::AllocationUsage
| Self::RequirementUsage
| Self::ConstraintUsage
| Self::StateUsage
| Self::TransitionUsage
| Self::CalculationUsage
| Self::ReferenceUsage
| Self::OccurrenceUsage
| Self::FlowConnectionUsage
| Self::SuccessionFlowConnectionUsage
| Self::Feature
| Self::Step
| Self::Expression
| Self::BooleanExpression
| Self::Invariant
)
}
pub fn is_relationship(&self) -> bool {
matches!(
self,
Self::Membership
| Self::OwningMembership
| Self::FeatureMembership
| Self::ReturnParameterMembership
| Self::ParameterMembership
| Self::EndFeatureMembership
| Self::ResultExpressionMembership
| Self::Import
| Self::NamespaceImport
| Self::MembershipImport
| Self::Specialization
| Self::FeatureTyping
| Self::Subsetting
| Self::ReferenceSubsetting
| Self::CrossSubsetting
| Self::Redefinition
| Self::Conjugation
| Self::FeatureValue
| Self::FeatureChaining
| Self::FeatureInverting
| Self::Intersecting
| Self::Disjoining
| Self::Unioning
| Self::Dependency
| Self::Satisfaction
| Self::Verification
| Self::Annotation
)
}
pub fn is_membership(&self) -> bool {
matches!(
self,
Self::OwningMembership
| Self::FeatureMembership
| Self::ReturnParameterMembership
| Self::ParameterMembership
| Self::EndFeatureMembership
| Self::ResultExpressionMembership
)
}
pub fn is_feature_kind(&self) -> bool {
self.is_usage()
|| matches!(
self,
Self::Connector
| Self::BindingConnector
| Self::Succession
| Self::Flow
| Self::MultiplicityRange
| Self::LiteralInteger
| Self::LiteralReal
| Self::LiteralInfinity
| Self::LiteralBoolean
| Self::LiteralString
| Self::NullExpression
| Self::FeatureReferenceExpression
| Self::OperatorExpression
| Self::InvocationExpression
| Self::FeatureChainExpression
| Self::ConstructorExpression
)
}
pub fn membership_kind_for(child_kind: ElementKind) -> ElementKind {
if child_kind.is_feature_kind() {
Self::FeatureMembership
} else {
Self::OwningMembership
}
}
pub fn is_inline_rendered(&self) -> bool {
matches!(
self,
Self::FeatureValue
| Self::FeatureTyping
| Self::Specialization
| Self::Redefinition
| Self::Subsetting
| Self::ReferenceSubsetting
| Self::CrossSubsetting
| Self::FeatureChaining
| Self::Conjugation
)
}
pub fn is_sysml(&self) -> bool {
matches!(
self,
Self::Namespace
| Self::Package
| Self::LibraryPackage
| Self::PartDefinition
| Self::ItemDefinition
| Self::ActionDefinition
| Self::PortDefinition
| Self::AttributeDefinition
| Self::ConnectionDefinition
| Self::InterfaceDefinition
| Self::AllocationDefinition
| Self::RequirementDefinition
| Self::ConstraintDefinition
| Self::UseCaseDefinition
| Self::ConcernDefinition
| Self::ViewDefinition
| Self::ViewpointDefinition
| Self::RenderingDefinition
| Self::StateDefinition
| Self::TransitionUsage
| Self::CalculationDefinition
| Self::AnalysisCaseDefinition
| Self::EnumerationDefinition
| Self::MetadataDefinition
| Self::PartUsage
| Self::ItemUsage
| Self::ActionUsage
| Self::PortUsage
| Self::AttributeUsage
| Self::ConnectionUsage
| Self::InterfaceUsage
| Self::AllocationUsage
| Self::RequirementUsage
| Self::ConstraintUsage
| Self::StateUsage
| Self::CalculationUsage
| Self::ReferenceUsage
| Self::OccurrenceUsage
| Self::FlowConnectionUsage
| Self::SuccessionFlowConnectionUsage
| Self::MetadataUsage
)
}
pub fn xmi_type(&self) -> &'static str {
match self {
Self::Namespace => "sysml:Namespace",
Self::Package => "sysml:Package",
Self::LibraryPackage => "sysml:LibraryPackage",
Self::Class => "kerml:Class",
Self::DataType => "kerml:DataType",
Self::Structure => "kerml:Structure",
Self::Association => "kerml:Association",
Self::AssociationStructure => "kerml:AssociationStructure",
Self::Interaction => "kerml:Interaction",
Self::Behavior => "kerml:Behavior",
Self::Function => "kerml:Function",
Self::Predicate => "kerml:Predicate",
Self::PartDefinition => "sysml:PartDefinition",
Self::ItemDefinition => "sysml:ItemDefinition",
Self::ActionDefinition => "sysml:ActionDefinition",
Self::PortDefinition => "sysml:PortDefinition",
Self::AttributeDefinition => "sysml:AttributeDefinition",
Self::ConnectionDefinition => "sysml:ConnectionDefinition",
Self::InterfaceDefinition => "sysml:InterfaceDefinition",
Self::AllocationDefinition => "sysml:AllocationDefinition",
Self::RequirementDefinition => "sysml:RequirementDefinition",
Self::ConstraintDefinition => "sysml:ConstraintDefinition",
Self::StateDefinition => "sysml:StateDefinition",
Self::CalculationDefinition => "sysml:CalculationDefinition",
Self::UseCaseDefinition => "sysml:UseCaseDefinition",
Self::AnalysisCaseDefinition => "sysml:AnalysisCaseDefinition",
Self::ConcernDefinition => "sysml:ConcernDefinition",
Self::ViewDefinition => "sysml:ViewDefinition",
Self::ViewpointDefinition => "sysml:ViewpointDefinition",
Self::RenderingDefinition => "sysml:RenderingDefinition",
Self::EnumerationDefinition => "sysml:EnumerationDefinition",
Self::MetadataDefinition => "sysml:MetadataDefinition",
Self::PartUsage => "sysml:PartUsage",
Self::ItemUsage => "sysml:ItemUsage",
Self::ActionUsage => "sysml:ActionUsage",
Self::PortUsage => "sysml:PortUsage",
Self::AttributeUsage => "sysml:AttributeUsage",
Self::ConnectionUsage => "sysml:ConnectionUsage",
Self::InterfaceUsage => "sysml:InterfaceUsage",
Self::AllocationUsage => "sysml:AllocationUsage",
Self::RequirementUsage => "sysml:RequirementUsage",
Self::ConstraintUsage => "sysml:ConstraintUsage",
Self::StateUsage => "sysml:StateUsage",
Self::TransitionUsage => "sysml:TransitionUsage",
Self::CalculationUsage => "sysml:CalculationUsage",
Self::ReferenceUsage => "sysml:ReferenceUsage",
Self::OccurrenceUsage => "sysml:OccurrenceUsage",
Self::FlowConnectionUsage => "sysml:FlowConnectionUsage",
Self::SuccessionFlowConnectionUsage => "sysml:SuccessionFlowConnectionUsage",
Self::Feature => "kerml:Feature",
Self::Step => "kerml:Step",
Self::Expression => "kerml:Expression",
Self::BooleanExpression => "kerml:BooleanExpression",
Self::Invariant => "kerml:Invariant",
Self::Connector => "kerml:Connector",
Self::BindingConnector => "kerml:BindingConnector",
Self::Succession => "kerml:Succession",
Self::Flow => "kerml:Flow",
Self::MultiplicityRange => "kerml:MultiplicityRange",
Self::LiteralInteger => "kerml:LiteralInteger",
Self::LiteralReal => "kerml:LiteralRational",
Self::LiteralInfinity => "kerml:LiteralInfinity",
Self::LiteralBoolean => "kerml:LiteralBoolean",
Self::LiteralString => "kerml:LiteralString",
Self::NullExpression => "kerml:NullExpression",
Self::FeatureReferenceExpression => "kerml:FeatureReferenceExpression",
Self::OperatorExpression => "kerml:OperatorExpression",
Self::InvocationExpression => "kerml:InvocationExpression",
Self::FeatureChainExpression => "kerml:FeatureChainExpression",
Self::ConstructorExpression => "kerml:ConstructorExpression",
Self::Membership => "kerml:Membership",
Self::OwningMembership => "kerml:OwningMembership",
Self::FeatureMembership => "kerml:FeatureMembership",
Self::ReturnParameterMembership => "kerml:ReturnParameterMembership",
Self::ParameterMembership => "kerml:ParameterMembership",
Self::EndFeatureMembership => "kerml:EndFeatureMembership",
Self::ResultExpressionMembership => "kerml:ResultExpressionMembership",
Self::Import => "kerml:Import",
Self::NamespaceImport => "kerml:NamespaceImport",
Self::MembershipImport => "kerml:MembershipImport",
Self::Specialization => "kerml:Specialization",
Self::FeatureTyping => "kerml:FeatureTyping",
Self::Subsetting => "kerml:Subsetting",
Self::ReferenceSubsetting => "kerml:ReferenceSubsetting",
Self::CrossSubsetting => "kerml:CrossSubsetting",
Self::Redefinition => "kerml:Redefinition",
Self::Conjugation => "kerml:Conjugation",
Self::FeatureValue => "kerml:FeatureValue",
Self::FeatureChaining => "kerml:FeatureChaining",
Self::FeatureInverting => "kerml:FeatureInverting",
Self::Intersecting => "kerml:Intersecting",
Self::Disjoining => "kerml:Disjoining",
Self::Unioning => "kerml:Unioning",
Self::Dependency => "kerml:Dependency",
Self::Satisfaction => "sysml:SatisfyRequirementUsage",
Self::Verification => "sysml:RequirementVerificationMembership",
Self::Comment => "kerml:Comment",
Self::Documentation => "kerml:Documentation",
Self::TextualRepresentation => "kerml:TextualRepresentation",
Self::MetadataUsage => "sysml:MetadataUsage",
Self::AnnotatingElement => "kerml:AnnotatingElement",
Self::Annotation => "kerml:Annotation",
Self::Classifier => "kerml:Classifier",
Self::Metaclass => "kerml:Metaclass",
Self::Alias => "kerml:Membership",
Self::Other => "kerml:Element",
}
}
pub fn xsi_type(&self) -> &'static str {
match self {
Self::Package => "sysml:Package",
Self::LibraryPackage => "sysml:LibraryPackage",
Self::Class => "sysml:Class",
Self::DataType => "sysml:DataType",
Self::Structure => "sysml:Structure",
Self::Association => "sysml:Association",
Self::AssociationStructure => "sysml:AssociationStructure",
Self::Interaction => "sysml:Interaction",
Self::Behavior => "sysml:Behavior",
Self::Function => "sysml:Function",
Self::Predicate => "sysml:Predicate",
Self::PartDefinition => "sysml:PartDefinition",
Self::ItemDefinition => "sysml:ItemDefinition",
Self::ActionDefinition => "sysml:ActionDefinition",
Self::PortDefinition => "sysml:PortDefinition",
Self::AttributeDefinition => "sysml:AttributeDefinition",
Self::ConnectionDefinition => "sysml:ConnectionDefinition",
Self::InterfaceDefinition => "sysml:InterfaceDefinition",
Self::AllocationDefinition => "sysml:AllocationDefinition",
Self::RequirementDefinition => "sysml:RequirementDefinition",
Self::ConstraintDefinition => "sysml:ConstraintDefinition",
Self::StateDefinition => "sysml:StateDefinition",
Self::CalculationDefinition => "sysml:CalculationDefinition",
Self::UseCaseDefinition => "sysml:UseCaseDefinition",
Self::AnalysisCaseDefinition => "sysml:AnalysisCaseDefinition",
Self::ConcernDefinition => "sysml:ConcernDefinition",
Self::ViewDefinition => "sysml:ViewDefinition",
Self::ViewpointDefinition => "sysml:ViewpointDefinition",
Self::RenderingDefinition => "sysml:RenderingDefinition",
Self::EnumerationDefinition => "sysml:EnumerationDefinition",
Self::MetadataDefinition => "sysml:MetadataDefinition",
Self::PartUsage => "sysml:PartUsage",
Self::ItemUsage => "sysml:ItemUsage",
Self::ActionUsage => "sysml:ActionUsage",
Self::PortUsage => "sysml:PortUsage",
Self::AttributeUsage => "sysml:AttributeUsage",
Self::ConnectionUsage => "sysml:ConnectionUsage",
Self::InterfaceUsage => "sysml:InterfaceUsage",
Self::AllocationUsage => "sysml:AllocationUsage",
Self::RequirementUsage => "sysml:RequirementUsage",
Self::ConstraintUsage => "sysml:ConstraintUsage",
Self::StateUsage => "sysml:StateUsage",
Self::TransitionUsage => "sysml:TransitionUsage",
Self::CalculationUsage => "sysml:CalculationUsage",
Self::ReferenceUsage => "sysml:ReferenceUsage",
Self::OccurrenceUsage => "sysml:OccurrenceUsage",
Self::FlowConnectionUsage => "sysml:FlowConnectionUsage",
Self::SuccessionFlowConnectionUsage => "sysml:SuccessionFlowConnectionUsage",
Self::Feature => "sysml:Feature",
Self::Step => "sysml:Step",
Self::Expression => "sysml:Expression",
Self::BooleanExpression => "sysml:BooleanExpression",
Self::Invariant => "sysml:Invariant",
Self::Connector => "sysml:Connector",
Self::BindingConnector => "sysml:BindingConnector",
Self::Succession => "sysml:Succession",
Self::Flow => "sysml:Flow",
Self::MultiplicityRange => "sysml:MultiplicityRange",
Self::LiteralInteger => "sysml:LiteralInteger",
Self::LiteralReal => "sysml:LiteralRational",
Self::LiteralInfinity => "sysml:LiteralInfinity",
Self::LiteralBoolean => "sysml:LiteralBoolean",
Self::LiteralString => "sysml:LiteralString",
Self::NullExpression => "sysml:NullExpression",
Self::FeatureReferenceExpression => "sysml:FeatureReferenceExpression",
Self::OperatorExpression => "sysml:OperatorExpression",
Self::InvocationExpression => "sysml:InvocationExpression",
Self::FeatureChainExpression => "sysml:FeatureChainExpression",
Self::ConstructorExpression => "sysml:ConstructorExpression",
Self::Membership => "sysml:Membership",
Self::OwningMembership => "sysml:OwningMembership",
Self::FeatureMembership => "sysml:FeatureMembership",
Self::ReturnParameterMembership => "sysml:ReturnParameterMembership",
Self::ParameterMembership => "sysml:ParameterMembership",
Self::EndFeatureMembership => "sysml:EndFeatureMembership",
Self::ResultExpressionMembership => "sysml:ResultExpressionMembership",
Self::Import => "sysml:Import",
Self::NamespaceImport => "sysml:NamespaceImport",
Self::MembershipImport => "sysml:MembershipImport",
Self::Specialization => "sysml:Subclassification",
Self::FeatureTyping => "sysml:FeatureTyping",
Self::Subsetting => "sysml:Subsetting",
Self::ReferenceSubsetting => "sysml:ReferenceSubsetting",
Self::CrossSubsetting => "sysml:CrossSubsetting",
Self::Redefinition => "sysml:Redefinition",
Self::Conjugation => "sysml:Conjugation",
Self::FeatureValue => "sysml:FeatureValue",
Self::FeatureChaining => "sysml:FeatureChaining",
Self::FeatureInverting => "sysml:FeatureInverting",
Self::Intersecting => "sysml:Intersecting",
Self::Disjoining => "sysml:Disjoining",
Self::Unioning => "sysml:Unioning",
Self::Dependency => "sysml:Dependency",
Self::Satisfaction => "sysml:SatisfyRequirementUsage",
Self::Verification => "sysml:RequirementVerificationMembership",
Self::Comment => "sysml:Comment",
Self::Documentation => "sysml:Documentation",
Self::TextualRepresentation => "sysml:TextualRepresentation",
Self::MetadataUsage => "sysml:MetadataUsage",
Self::AnnotatingElement => "sysml:AnnotatingElement",
Self::Annotation => "sysml:Annotation",
Self::Classifier => "sysml:Classifier",
Self::Metaclass => "sysml:Metaclass",
Self::Alias => "sysml:Membership",
Self::Other => "sysml:Element",
Self::Namespace => "sysml:Namespace",
}
}
pub fn from_xmi_type(xmi_type: &str) -> Self {
let type_name = xmi_type.rsplit(':').next().unwrap_or(xmi_type);
match type_name {
"Namespace" => Self::Namespace,
"Package" => Self::Package,
"LibraryPackage" => Self::LibraryPackage,
"Class" => Self::Class,
"DataType" => Self::DataType,
"Structure" => Self::Structure,
"Association" => Self::Association,
"AssociationStructure" => Self::AssociationStructure,
"Interaction" => Self::Interaction,
"Behavior" => Self::Behavior,
"Function" => Self::Function,
"Predicate" => Self::Predicate,
"PartDefinition" => Self::PartDefinition,
"ItemDefinition" => Self::ItemDefinition,
"ActionDefinition" => Self::ActionDefinition,
"PortDefinition" => Self::PortDefinition,
"AttributeDefinition" => Self::AttributeDefinition,
"ConnectionDefinition" => Self::ConnectionDefinition,
"InterfaceDefinition" => Self::InterfaceDefinition,
"AllocationDefinition" => Self::AllocationDefinition,
"RequirementDefinition" => Self::RequirementDefinition,
"ConstraintDefinition" => Self::ConstraintDefinition,
"StateDefinition" => Self::StateDefinition,
"CalculationDefinition" => Self::CalculationDefinition,
"UseCaseDefinition" => Self::UseCaseDefinition,
"AnalysisCaseDefinition" => Self::AnalysisCaseDefinition,
"ConcernDefinition" => Self::ConcernDefinition,
"ViewDefinition" => Self::ViewDefinition,
"ViewpointDefinition" => Self::ViewpointDefinition,
"RenderingDefinition" => Self::RenderingDefinition,
"EnumerationDefinition" => Self::EnumerationDefinition,
"MetadataDefinition" => Self::MetadataDefinition,
"PartUsage" => Self::PartUsage,
"ItemUsage" => Self::ItemUsage,
"ActionUsage" => Self::ActionUsage,
"PortUsage" => Self::PortUsage,
"AttributeUsage" => Self::AttributeUsage,
"ConnectionUsage" => Self::ConnectionUsage,
"InterfaceUsage" => Self::InterfaceUsage,
"AllocationUsage" => Self::AllocationUsage,
"RequirementUsage" => Self::RequirementUsage,
"ConstraintUsage" => Self::ConstraintUsage,
"StateUsage" => Self::StateUsage,
"TransitionUsage" => Self::TransitionUsage,
"CalculationUsage" => Self::CalculationUsage,
"ReferenceUsage" => Self::ReferenceUsage,
"OccurrenceUsage" => Self::OccurrenceUsage,
"FlowConnectionUsage" => Self::FlowConnectionUsage,
"SuccessionFlowConnectionUsage" => Self::SuccessionFlowConnectionUsage,
"Feature" => Self::Feature,
"Step" => Self::Step,
"Expression" => Self::Expression,
"BooleanExpression" => Self::BooleanExpression,
"Invariant" => Self::Invariant,
"Connector" => Self::Connector,
"BindingConnector" => Self::BindingConnector,
"Succession" => Self::Succession,
"Flow" => Self::Flow,
"MultiplicityRange" => Self::MultiplicityRange,
"LiteralInteger" => Self::LiteralInteger,
"LiteralRational" | "LiteralReal" => Self::LiteralReal,
"LiteralInfinity" => Self::LiteralInfinity,
"LiteralBoolean" => Self::LiteralBoolean,
"LiteralString" => Self::LiteralString,
"NullExpression" => Self::NullExpression,
"FeatureReferenceExpression" => Self::FeatureReferenceExpression,
"OperatorExpression" => Self::OperatorExpression,
"InvocationExpression" => Self::InvocationExpression,
"FeatureChainExpression" => Self::FeatureChainExpression,
"ConstructorExpression" => Self::ConstructorExpression,
"Membership" => Self::Membership,
"OwningMembership" => Self::OwningMembership,
"FeatureMembership" => Self::FeatureMembership,
"ReturnParameterMembership" => Self::ReturnParameterMembership,
"ParameterMembership" => Self::ParameterMembership,
"EndFeatureMembership" => Self::EndFeatureMembership,
"ResultExpressionMembership" => Self::ResultExpressionMembership,
"Import" => Self::Import,
"NamespaceImport" => Self::NamespaceImport,
"MembershipImport" => Self::MembershipImport,
"Specialization" | "Subclassification" => Self::Specialization,
"FeatureTyping" => Self::FeatureTyping,
"Subsetting" => Self::Subsetting,
"ReferenceSubsetting" => Self::ReferenceSubsetting,
"CrossSubsetting" => Self::CrossSubsetting,
"Redefinition" => Self::Redefinition,
"Conjugation" => Self::Conjugation,
"FeatureValue" => Self::FeatureValue,
"FeatureChaining" => Self::FeatureChaining,
"FeatureInverting" => Self::FeatureInverting,
"Intersecting" => Self::Intersecting,
"Disjoining" => Self::Disjoining,
"Unioning" => Self::Unioning,
"Dependency" => Self::Dependency,
"SatisfyRequirementUsage" => Self::Satisfaction,
"RequirementVerificationMembership" => Self::Verification,
"Comment" => Self::Comment,
"Documentation" => Self::Documentation,
"TextualRepresentation" => Self::TextualRepresentation,
"MetadataUsage" => Self::MetadataUsage,
"AnnotatingElement" => Self::AnnotatingElement,
"Annotation" => Self::Annotation,
"Classifier" => Self::Classifier,
"Metaclass" => Self::Metaclass,
_ => Self::Other,
}
}
pub fn jsonld_type(&self) -> &'static str {
self.xmi_type().rsplit(':').next().unwrap_or("Element")
}
}
#[derive(Clone, Debug)]
pub struct Element {
pub id: ElementId,
pub kind: ElementKind,
pub name: Option<Arc<str>>,
pub short_name: Option<Arc<str>>,
pub qualified_name: Option<Arc<str>>,
pub owner: Option<ElementId>,
pub owned_elements: Vec<ElementId>,
pub documentation: Option<Arc<str>>,
pub is_abstract: bool,
pub is_variation: bool,
pub is_derived: bool,
pub is_readonly: bool,
pub is_parallel: bool,
pub is_individual: bool,
pub is_end: bool,
pub is_default: bool,
pub is_ordered: bool,
pub is_nonunique: bool,
pub is_portion: bool,
pub visibility: Visibility,
pub properties: IndexMap<Arc<str>, PropertyValue>,
pub relationship: Option<RelationshipData>,
}
impl Element {
pub fn new(id: impl Into<ElementId>, kind: ElementKind) -> Self {
Self {
id: id.into(),
kind,
name: None,
short_name: None,
qualified_name: None,
owner: None,
owned_elements: Vec::new(),
documentation: None,
is_abstract: false,
is_variation: false,
is_derived: false,
is_readonly: false,
is_parallel: false,
is_individual: false,
is_end: false,
is_default: false,
is_ordered: false,
is_nonunique: false,
is_portion: false,
visibility: Visibility::Public,
properties: IndexMap::new(),
relationship: None,
}
}
pub fn new_relationship(
id: impl Into<ElementId>,
kind: ElementKind,
source: impl Into<ElementId>,
target: impl Into<ElementId>,
) -> Self {
Self {
relationship: Some(RelationshipData::new(source, target)),
..Self::new(id, kind)
}
}
pub fn is_relationship_element(&self) -> bool {
self.relationship.is_some()
}
pub fn as_relationship(&self) -> Option<&RelationshipData> {
self.relationship.as_ref()
}
pub fn as_relationship_mut(&mut self) -> Option<&mut RelationshipData> {
self.relationship.as_mut()
}
pub fn source(&self) -> Option<&ElementId> {
self.relationship.as_ref()?.source()
}
pub fn target(&self) -> Option<&ElementId> {
self.relationship.as_ref()?.target()
}
pub fn with_relationship(
mut self,
source: impl Into<ElementId>,
target: impl Into<ElementId>,
) -> Self {
self.relationship = Some(RelationshipData::new(source, target));
self
}
pub fn with_name(mut self, name: impl Into<Arc<str>>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_qualified_name(mut self, qualified_name: impl Into<Arc<str>>) -> Self {
self.qualified_name = Some(qualified_name.into());
self
}
pub fn with_short_name(mut self, short_name: impl Into<Arc<str>>) -> Self {
self.short_name = Some(short_name.into());
self
}
pub fn with_owner(mut self, owner: impl Into<ElementId>) -> Self {
self.owner = Some(owner.into());
self
}
pub fn with_owner_opt(mut self, owner: Option<ElementId>) -> Self {
self.owner = owner;
self
}
pub fn with_owned(mut self, owned: impl Into<ElementId>) -> Self {
self.owned_elements.push(owned.into());
self
}
pub fn with_property(mut self, key: impl Into<Arc<str>>, value: PropertyValue) -> Self {
self.properties.insert(key.into(), value);
self
}
pub fn set_abstract(&mut self, value: bool) {
self.is_abstract = value;
self.properties
.insert(Arc::from("isAbstract"), PropertyValue::Boolean(value));
}
pub fn set_variation(&mut self, value: bool) {
self.is_variation = value;
self.properties
.insert(Arc::from("isVariation"), PropertyValue::Boolean(value));
}
pub fn set_derived(&mut self, value: bool) {
self.is_derived = value;
self.properties
.insert(Arc::from("isDerived"), PropertyValue::Boolean(value));
}
pub fn set_readonly(&mut self, value: bool) {
self.is_readonly = value;
self.properties
.insert(Arc::from("isReadOnly"), PropertyValue::Boolean(value));
}
pub fn set_parallel(&mut self, value: bool) {
self.is_parallel = value;
self.properties
.insert(Arc::from("isParallel"), PropertyValue::Boolean(value));
}
pub fn set_individual(&mut self, value: bool) {
self.is_individual = value;
self.properties
.insert(Arc::from("isIndividual"), PropertyValue::Boolean(value));
}
pub fn set_end(&mut self, value: bool) {
self.is_end = value;
self.properties
.insert(Arc::from("isEnd"), PropertyValue::Boolean(value));
}
pub fn set_default(&mut self, value: bool) {
self.is_default = value;
self.properties
.insert(Arc::from("isDefault"), PropertyValue::Boolean(value));
}
pub fn set_ordered(&mut self, value: bool) {
self.is_ordered = value;
self.properties
.insert(Arc::from("isOrdered"), PropertyValue::Boolean(value));
}
pub fn set_nonunique(&mut self, value: bool) {
self.is_nonunique = value;
self.properties
.insert(Arc::from("isNonunique"), PropertyValue::Boolean(value));
}
pub fn set_portion(&mut self, value: bool) {
self.is_portion = value;
self.properties
.insert(Arc::from("isPortion"), PropertyValue::Boolean(value));
}
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash)]
pub enum Visibility {
#[default]
Public,
Private,
Protected,
}
#[derive(Clone, Debug, PartialEq)]
pub enum PropertyValue {
String(Arc<str>),
Integer(i64),
Real(f64),
Boolean(bool),
Reference(ElementId),
List(Vec<PropertyValue>),
}
impl From<&str> for PropertyValue {
fn from(s: &str) -> Self {
Self::String(s.into())
}
}
impl From<String> for PropertyValue {
fn from(s: String) -> Self {
Self::String(s.into())
}
}
impl From<i64> for PropertyValue {
fn from(v: i64) -> Self {
Self::Integer(v)
}
}
impl From<f64> for PropertyValue {
fn from(v: f64) -> Self {
Self::Real(v)
}
}
impl From<bool> for PropertyValue {
fn from(v: bool) -> Self {
Self::Boolean(v)
}
}
impl From<ElementId> for PropertyValue {
fn from(id: ElementId) -> Self {
Self::Reference(id)
}
}
#[derive(Clone, Debug, PartialEq, Eq)]
pub struct RelationshipData {
pub source: Vec<ElementId>,
pub target: Vec<ElementId>,
}
impl RelationshipData {
pub fn new(source: impl Into<ElementId>, target: impl Into<ElementId>) -> Self {
Self {
source: vec![source.into()],
target: vec![target.into()],
}
}
pub fn new_multi(sources: Vec<ElementId>, targets: Vec<ElementId>) -> Self {
Self {
source: sources,
target: targets,
}
}
pub fn source(&self) -> Option<&ElementId> {
self.source.first()
}
pub fn target(&self) -> Option<&ElementId> {
self.target.first()
}
}
#[derive(Clone, Debug, Default)]
pub struct Model {
pub elements: IndexMap<ElementId, Element>,
pub roots: Vec<ElementId>,
pub metadata: ModelMetadata,
}
impl Model {
pub fn new() -> Self {
Self::default()
}
pub fn add_element(&mut self, element: Element) -> &ElementId {
let id = element.id.clone();
if element.owner.is_none() {
self.roots.push(id.clone());
}
self.elements.insert(id.clone(), element);
&self.elements.get(&id).unwrap().id
}
pub fn add_rel(
&mut self,
id: impl Into<ElementId>,
kind: ElementKind,
source: impl Into<ElementId>,
target: impl Into<ElementId>,
owner: Option<ElementId>,
) -> ElementId {
let id = id.into();
let element = Element::new_relationship(id.clone(), kind, source.into(), target.into())
.with_owner_opt(owner);
self.elements.insert(id.clone(), element);
id
}
pub fn get(&self, id: &ElementId) -> Option<&Element> {
self.elements.get(id)
}
pub fn get_mut(&mut self, id: &ElementId) -> Option<&mut Element> {
self.elements.get_mut(id)
}
pub fn iter_elements(&self) -> impl Iterator<Item = &Element> {
self.elements.values()
}
pub fn iter_roots(&self) -> impl Iterator<Item = &Element> {
self.roots.iter().filter_map(|id| self.elements.get(id))
}
pub fn rel_elements_from<'a>(
&'a self,
source: &'a ElementId,
) -> impl Iterator<Item = &'a Element> {
self.elements.values().filter(move |e| {
e.relationship
.as_ref()
.is_some_and(|rd| rd.source.contains(source))
})
}
pub fn rel_elements_to<'a>(
&'a self,
target: &'a ElementId,
) -> impl Iterator<Item = &'a Element> {
self.elements.values().filter(move |e| {
e.relationship
.as_ref()
.is_some_and(|rd| rd.target.contains(target))
})
}
pub fn rel_elements_of_kind<'a>(
&'a self,
source: &'a ElementId,
kind: ElementKind,
) -> impl Iterator<Item = &'a Element> {
self.rel_elements_from(source)
.filter(move |e| e.kind == kind)
}
pub fn rel_target<'a>(&'a self, rel_element: &'a Element) -> Option<&'a Element> {
rel_element.target().and_then(|tid| self.get(tid))
}
pub fn rel_elements_owned_by<'a>(
&'a self,
owner_id: &'a ElementId,
kind: ElementKind,
) -> impl Iterator<Item = &'a Element> {
self.elements.values().filter(move |e| {
e.kind == kind && e.relationship.is_some() && e.owner.as_ref() == Some(owner_id)
})
}
pub fn element_count(&self) -> usize {
self.elements.len()
}
pub fn relationship_count(&self) -> usize {
self.elements
.values()
.filter(|e| e.relationship.is_some())
.count()
}
pub fn iter_relationship_elements(&self) -> impl Iterator<Item = &Element> {
self.elements.values().filter(|e| e.relationship.is_some())
}
pub fn owned_members(&self, id: &ElementId) -> Vec<&Element> {
let element = match self.get(id) {
Some(e) => e,
None => return Vec::new(),
};
let mut result = Vec::new();
for child_id in &element.owned_elements {
if let Some(child) = self.get(child_id) {
if child.kind.is_membership() {
for grandchild_id in &child.owned_elements {
if let Some(gc) = self.get(grandchild_id) {
if !gc.kind.is_relationship() {
result.push(gc);
}
}
}
} else if !child.kind.is_relationship() {
result.push(child);
}
}
}
result
}
pub fn wrap_children_in_memberships(&mut self) {
let to_wrap: Vec<(ElementId, ElementKind, ElementId)> = self
.elements
.values()
.filter(|e| {
if let Some(ref owner_id) = e.owner {
if e.kind.is_relationship() {
return false;
}
if e.kind.is_membership() {
return false;
}
if let Some(owner) = self.elements.get(owner_id) {
return !owner.kind.is_membership() && !owner.kind.is_relationship();
}
}
false
})
.map(|e| (e.id.clone(), e.kind, e.owner.clone().unwrap()))
.collect();
for (child_id, child_kind, parent_id) in to_wrap {
let m_kind = ElementKind::membership_kind_for(child_kind);
let m_id = ElementId::new(format!("{}-m", child_id.as_str()));
let mut membership = Element::new(m_id.clone(), m_kind);
membership.owner = Some(parent_id.clone());
membership.owned_elements.push(child_id.clone());
if let Some(child) = self.elements.get_mut(&child_id) {
child.owner = Some(m_id.clone());
}
if let Some(parent) = self.elements.get_mut(&parent_id) {
if let Some(pos) = parent.owned_elements.iter().position(|id| *id == child_id) {
parent.owned_elements[pos] = m_id.clone();
}
}
self.elements.insert(m_id, membership);
}
}
}
#[derive(Clone, Debug, Default)]
pub struct ModelMetadata {
pub name: Option<String>,
pub version: Option<String>,
pub description: Option<String>,
pub uri: Option<String>,
pub sysml_version: Option<String>,
pub tool: Option<String>,
pub created: Option<String>,
pub modified: Option<String>,
pub declared_namespaces: std::collections::HashMap<String, String>,
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_element_id_generation() {
let id1 = ElementId::generate();
let id2 = ElementId::generate();
assert_ne!(id1, id2);
}
#[test]
fn test_element_builder() {
let element = Element::new("pkg1", ElementKind::Package)
.with_name("MyPackage")
.with_short_name("mp");
assert_eq!(element.id.as_str(), "pkg1");
assert_eq!(element.name.as_deref(), Some("MyPackage"));
assert_eq!(element.short_name.as_deref(), Some("mp"));
assert_eq!(element.kind, ElementKind::Package);
}
#[test]
fn test_model_add_elements() {
let mut model = Model::new();
let pkg = Element::new("pkg1", ElementKind::Package).with_name("Root");
model.add_element(pkg);
let part = Element::new("part1", ElementKind::PartDefinition)
.with_name("Vehicle")
.with_owner("pkg1");
model.add_element(part);
assert_eq!(model.element_count(), 2);
assert_eq!(model.roots.len(), 1);
assert_eq!(
model.get(&ElementId::new("pkg1")).unwrap().name.as_deref(),
Some("Root")
);
}
#[test]
fn test_model_relationships() {
let mut model = Model::new();
model.add_element(Element::new("def1", ElementKind::PartDefinition).with_name("Base"));
model.add_element(Element::new("def2", ElementKind::PartDefinition).with_name("Derived"));
model.add_rel("rel1", ElementKind::Specialization, "def2", "def1", None);
assert_eq!(model.relationship_count(), 1);
let source_id = ElementId::new("def2");
let rels: Vec<_> = model.rel_elements_from(&source_id).collect();
assert_eq!(rels.len(), 1);
assert_eq!(rels[0].target().unwrap().as_str(), "def1");
}
#[test]
fn test_element_kind_xmi_roundtrip() {
let kinds = [
ElementKind::Package,
ElementKind::PartDefinition,
ElementKind::ActionUsage,
ElementKind::Specialization,
];
for kind in kinds {
let xmi_type = kind.xmi_type();
let parsed = ElementKind::from_xmi_type(xmi_type);
assert_eq!(kind, parsed, "Failed roundtrip for {xmi_type}");
}
}
#[test]
fn test_relationship_data_new() {
let rd = RelationshipData::new("src1", "tgt1");
assert_eq!(rd.source().unwrap().as_str(), "src1");
assert_eq!(rd.target().unwrap().as_str(), "tgt1");
assert_eq!(rd.source.len(), 1);
assert_eq!(rd.target.len(), 1);
}
#[test]
fn test_relationship_data_multi() {
let rd = RelationshipData::new_multi(
vec![ElementId::new("s1"), ElementId::new("s2")],
vec![ElementId::new("t1")],
);
assert_eq!(rd.source.len(), 2);
assert_eq!(rd.target.len(), 1);
assert_eq!(rd.source().unwrap().as_str(), "s1");
}
#[test]
fn test_element_new_has_no_relationship_data() {
let e = Element::new("e1", ElementKind::Package);
assert!(!e.is_relationship_element());
assert!(e.as_relationship().is_none());
assert!(e.source().is_none());
assert!(e.target().is_none());
}
#[test]
fn test_element_new_relationship() {
let e = Element::new_relationship("rel1", ElementKind::Specialization, "derived", "base");
assert!(e.is_relationship_element());
assert_eq!(e.source().unwrap().as_str(), "derived");
assert_eq!(e.target().unwrap().as_str(), "base");
assert_eq!(e.kind, ElementKind::Specialization);
}
#[test]
fn test_element_with_relationship_builder() {
let e = Element::new("rel2", ElementKind::FeatureTyping)
.with_name("typing1")
.with_relationship("feature1", "type1");
assert!(e.is_relationship_element());
assert_eq!(e.name.as_deref(), Some("typing1"));
assert_eq!(e.source().unwrap().as_str(), "feature1");
assert_eq!(e.target().unwrap().as_str(), "type1");
}
#[test]
fn test_element_as_relationship_mut() {
let mut e = Element::new_relationship("rel3", ElementKind::Subsetting, "sub", "super");
let rd = e.as_relationship_mut().unwrap();
rd.target.push(ElementId::new("super2"));
assert_eq!(e.as_relationship().unwrap().target.len(), 2);
}
#[test]
fn test_add_rel_creates_element() {
let mut model = Model::new();
model.add_element(Element::new("a", ElementKind::PartDefinition).with_name("A"));
model.add_element(Element::new("b", ElementKind::PartDefinition).with_name("B"));
model.add_rel("rel1", ElementKind::Specialization, "b", "a", None);
assert_eq!(model.relationship_count(), 1);
let rel_el = model.get(&ElementId::new("rel1")).unwrap();
assert!(rel_el.is_relationship_element());
assert_eq!(rel_el.kind, ElementKind::Specialization);
assert_eq!(rel_el.source().unwrap().as_str(), "b");
assert_eq!(rel_el.target().unwrap().as_str(), "a");
}
#[test]
fn test_add_rel_preserves_owner() {
let mut model = Model::new();
model.add_element(Element::new("feat", ElementKind::Feature));
model.add_element(Element::new("typ", ElementKind::Class));
model.add_rel(
"rel_t",
ElementKind::FeatureTyping,
"feat",
"typ",
Some(ElementId::new("feat")),
);
let el = model.get(&ElementId::new("rel_t")).unwrap();
assert_eq!(el.owner.as_ref().unwrap().as_str(), "feat");
}
#[test]
fn test_add_rel_does_not_duplicate() {
let mut model = Model::new();
let existing = Element::new("rel_x", ElementKind::FeatureTyping).with_name("pre-existing");
model.add_element(existing);
model.add_rel("rel_x", ElementKind::FeatureTyping, "s", "t", None);
let el = model.get(&ElementId::new("rel_x")).unwrap();
assert!(el.is_relationship_element());
assert_eq!(model.relationship_count(), 1);
}
#[test]
fn test_relationship_element_kinds_are_relationships() {
let kinds = [
ElementKind::Specialization,
ElementKind::FeatureTyping,
ElementKind::Subsetting,
ElementKind::Redefinition,
ElementKind::Membership,
ElementKind::OwningMembership,
ElementKind::FeatureMembership,
ElementKind::NamespaceImport,
ElementKind::MembershipImport,
ElementKind::FeatureChaining,
ElementKind::Disjoining,
ElementKind::Dependency,
ElementKind::Satisfaction,
ElementKind::Verification,
];
for ek in kinds {
assert!(
ek.is_relationship(),
"{:?} should be a relationship ElementKind",
ek
);
}
}
}