use std::collections::HashMap;
use std::str::FromStr;
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum GraphType {
Directed,
Undirected,
Mixed,
}
impl GraphType {
pub fn is_directed(self) -> bool {
matches!(self, GraphType::Directed | GraphType::Mixed)
}
pub fn is_undirected(self) -> bool {
matches!(self, GraphType::Undirected)
}
pub fn is_mixed(self) -> bool {
matches!(self, GraphType::Mixed)
}
}
impl std::fmt::Display for GraphType {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
match self {
GraphType::Directed => write!(f, "Directed"),
GraphType::Undirected => write!(f, "Undirected"),
GraphType::Mixed => write!(f, "Mixed"),
}
}
}
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum Capability {
Parallel,
Distributed,
IncrementalUpdate,
Transactions,
Persistence,
Partitioning,
DynamicMode,
StaticMode,
WeightedEdges,
SelfLoops,
MultiEdges,
NodeAttributes,
EdgeAttributes,
Temporal,
Streaming,
DifferentiableStructure,
LieGroupOrthogonalization,
TensorRingCompression,
TopologyDefectDetection,
AttentionAnalysis,
}
impl Capability {
pub fn as_str(&self) -> &'static str {
match self {
Capability::Parallel => "parallel",
Capability::Distributed => "distributed",
Capability::IncrementalUpdate => "incremental_update",
Capability::Transactions => "transactions",
Capability::Persistence => "persistence",
Capability::Partitioning => "partitioning",
Capability::DynamicMode => "dynamic_mode",
Capability::StaticMode => "static_mode",
Capability::WeightedEdges => "weighted_edges",
Capability::SelfLoops => "self_loops",
Capability::MultiEdges => "multi_edges",
Capability::NodeAttributes => "node_attributes",
Capability::EdgeAttributes => "edge_attributes",
Capability::Temporal => "temporal",
Capability::Streaming => "streaming",
Capability::DifferentiableStructure => "differentiable_structure",
Capability::LieGroupOrthogonalization => "lie_group_orthogonalization",
Capability::TensorRingCompression => "tensor_ring_compression",
Capability::TopologyDefectDetection => "topology_defect_detection",
Capability::AttentionAnalysis => "attention_analysis",
}
}
}
impl FromStr for Capability {
type Err = ();
fn from_str(s: &str) -> Result<Self, Self::Err> {
match s {
"parallel" => Ok(Capability::Parallel),
"distributed" => Ok(Capability::Distributed),
"incremental_update" => Ok(Capability::IncrementalUpdate),
"transactions" => Ok(Capability::Transactions),
"persistence" => Ok(Capability::Persistence),
"partitioning" => Ok(Capability::Partitioning),
"dynamic_mode" => Ok(Capability::DynamicMode),
"static_mode" => Ok(Capability::StaticMode),
"weighted_edges" => Ok(Capability::WeightedEdges),
"self_loops" => Ok(Capability::SelfLoops),
"multi_edges" => Ok(Capability::MultiEdges),
"node_attributes" => Ok(Capability::NodeAttributes),
"edge_attributes" => Ok(Capability::EdgeAttributes),
"temporal" => Ok(Capability::Temporal),
"streaming" => Ok(Capability::Streaming),
"differentiable_structure" => Ok(Capability::DifferentiableStructure),
"lie_group_orthogonalization" => Ok(Capability::LieGroupOrthogonalization),
"tensor_ring_compression" => Ok(Capability::TensorRingCompression),
"topology_defect_detection" => Ok(Capability::TopologyDefectDetection),
"attention_analysis" => Ok(Capability::AttentionAnalysis),
_ => Err(()),
}
}
}
impl std::fmt::Display for Capability {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.as_str())
}
}
#[derive(Debug, Clone)]
pub struct GraphMetadata {
pub id: String,
pub name: Option<String>,
pub graph_type: GraphType,
pub node_count: Option<usize>,
pub edge_count: Option<usize>,
pub capabilities: Vec<Capability>,
pub custom: HashMap<String, String>,
}
impl GraphMetadata {
pub fn new(id: impl Into<String>, graph_type: GraphType) -> Self {
Self {
id: id.into(),
name: None,
graph_type,
node_count: None,
edge_count: None,
capabilities: Vec::new(),
custom: HashMap::new(),
}
}
pub fn with_name(mut self, name: impl Into<String>) -> Self {
self.name = Some(name.into());
self
}
pub fn with_node_count(mut self, count: usize) -> Self {
self.node_count = Some(count);
self
}
pub fn with_edge_count(mut self, count: usize) -> Self {
self.edge_count = Some(count);
self
}
pub fn with_capability(mut self, capability: Capability) -> Self {
self.capabilities.push(capability);
self
}
pub fn with_capabilities(mut self, capabilities: impl IntoIterator<Item = Capability>) -> Self {
self.capabilities.extend(capabilities);
self
}
pub fn with_custom(mut self, key: impl Into<String>, value: impl Into<String>) -> Self {
self.custom.insert(key.into(), value.into());
self
}
pub fn supports(&self, capability: Capability) -> bool {
self.capabilities.contains(&capability)
}
pub fn supports_all(&self, capabilities: &[Capability]) -> bool {
capabilities.iter().all(|&c| self.supports(c))
}
pub fn supports_any(&self, capabilities: &[Capability]) -> bool {
capabilities.iter().any(|&c| self.supports(c))
}
}
impl std::fmt::Display for GraphMetadata {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "GraphMetadata {{ ")?;
write!(f, "id: {}, ", self.id)?;
if let Some(name) = &self.name {
write!(f, "name: {}, ", name)?;
}
write!(f, "type: {}", self.graph_type)?;
if let Some(count) = self.node_count {
write!(f, ", nodes: {}", count)?;
}
if let Some(count) = self.edge_count {
write!(f, ", edges: {}", count)?;
}
if !self.capabilities.is_empty() {
write!(f, ", capabilities: [")?;
for (i, cap) in self.capabilities.iter().enumerate() {
if i > 0 {
write!(f, ", ")?;
}
write!(f, "{}", cap)?;
}
write!(f, "]")?;
}
write!(f, " }}")
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_graph_type() {
assert!(GraphType::Directed.is_directed());
assert!(!GraphType::Directed.is_undirected());
assert!(GraphType::Undirected.is_undirected());
assert!(!GraphType::Undirected.is_directed());
assert!(GraphType::Mixed.is_directed());
assert!(!GraphType::Mixed.is_undirected());
}
#[test]
fn test_capability_from_str() {
assert_eq!(Capability::from_str("parallel"), Ok(Capability::Parallel));
assert_eq!(
Capability::from_str("distributed"),
Ok(Capability::Distributed)
);
assert!(Capability::from_str("invalid").is_err());
}
#[test]
fn test_graph_metadata() {
let metadata = GraphMetadata::new("test_graph", GraphType::Directed)
.with_name("Test Graph")
.with_node_count(100)
.with_edge_count(500)
.with_capability(Capability::Parallel)
.with_capability(Capability::IncrementalUpdate)
.with_custom("version".to_string(), "1.0".to_string());
assert_eq!(metadata.id, "test_graph");
assert_eq!(metadata.name, Some("Test Graph".to_string()));
assert_eq!(metadata.node_count, Some(100));
assert_eq!(metadata.edge_count, Some(500));
assert!(metadata.supports(Capability::Parallel));
assert!(metadata.supports(Capability::IncrementalUpdate));
assert!(!metadata.supports(Capability::Distributed));
assert_eq!(metadata.custom.get("version"), Some(&"1.0".to_string()));
}
#[test]
fn test_metadata_supports_all() {
let metadata = GraphMetadata::new("test", GraphType::Undirected)
.with_capability(Capability::Parallel)
.with_capability(Capability::IncrementalUpdate);
assert!(metadata.supports_all(&[Capability::Parallel, Capability::IncrementalUpdate]));
assert!(!metadata.supports_all(&[Capability::Parallel, Capability::Distributed]));
}
#[test]
fn test_metadata_supports_any() {
let metadata =
GraphMetadata::new("test", GraphType::Undirected).with_capability(Capability::Parallel);
assert!(metadata.supports_any(&[Capability::Parallel, Capability::Distributed]));
assert!(!metadata.supports_any(&[Capability::Distributed, Capability::Persistence]));
}
}