use std::collections::BTreeMap;
use bincode::{Decode, Encode};
use serde::{Deserialize, Serialize};
use super::address::{DimensionVector, RevisionId, SpaceId};
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
pub struct HyperedgeId(pub u64);
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
pub struct HyperedgeKind(pub String);
impl HyperedgeKind {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize, Encode, Decode)]
pub struct EndpointRole(pub String);
impl EndpointRole {
pub fn new(value: impl Into<String>) -> Self {
Self(value.into())
}
pub fn as_str(&self) -> &str {
&self.0
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize, Encode, Decode)]
pub struct EndpointRef {
pub role: EndpointRole,
pub space: SpaceId,
pub node: DimensionVector,
}
#[derive(Debug, Clone, Serialize, Deserialize, Encode, Decode)]
pub struct Hyperedge {
pub id: HyperedgeId,
pub kind: HyperedgeKind,
pub endpoints: Vec<EndpointRef>,
pub weight_milli: Option<i64>,
pub metadata: BTreeMap<String, String>,
pub valid_from: RevisionId,
pub valid_to: Option<RevisionId>,
}
impl Hyperedge {
pub fn validate(&self) -> Result<(), HyperedgeValidationError> {
if self.endpoints.len() < 2 {
return Err(HyperedgeValidationError::TooFewEndpoints);
}
if self.kind.0.trim().is_empty() {
return Err(HyperedgeValidationError::EmptyKind);
}
if self.endpoints.iter().any(|ep| ep.role.0.trim().is_empty()) {
return Err(HyperedgeValidationError::EmptyEndpointRole);
}
if let Some(valid_to) = self.valid_to {
if valid_to < self.valid_from {
return Err(HyperedgeValidationError::InvalidValidityWindow {
valid_from: self.valid_from,
valid_to,
});
}
}
Ok(())
}
pub fn is_active_at(&self, revision: RevisionId) -> bool {
revision >= self.valid_from
&& self.valid_to.map(|to| revision <= to).unwrap_or(true)
}
}
#[derive(Debug)]
pub enum HyperedgeValidationError {
TooFewEndpoints,
EmptyKind,
EmptyEndpointRole,
InvalidValidityWindow {
valid_from: RevisionId,
valid_to: RevisionId,
},
}