use std::collections::BTreeSet;
use serde::{Deserialize, Serialize};
use crate::memory::MemoryRef;
use crate::partition::PartitionPath;
use crate::summary::{SummaryRef, SummarySubject};
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
pub enum NodeRef {
Memory(MemoryRef),
Summary(SummaryRef),
Partition(PartitionPath),
}
#[non_exhaustive]
#[derive(Debug, Clone, PartialEq, Eq, Hash, Serialize, Deserialize)]
#[serde(tag = "kind", content = "value", rename_all = "snake_case")]
pub enum EdgeKind {
Link,
SummaryInput {
from_subject: SummarySubject,
},
ParentPartition,
PartitionContains,
}
impl EdgeKind {
#[must_use]
pub fn tag(&self) -> EdgeKindTag {
match self {
EdgeKind::Link => EdgeKindTag::Link,
EdgeKind::SummaryInput { .. } => EdgeKindTag::SummaryInput,
EdgeKind::ParentPartition => EdgeKindTag::ParentPartition,
EdgeKind::PartitionContains => EdgeKindTag::PartitionContains,
}
}
}
#[non_exhaustive]
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, Serialize, Deserialize)]
#[serde(rename_all = "snake_case")]
pub enum EdgeKindTag {
Link,
SummaryInput,
ParentPartition,
PartitionContains,
}
impl EdgeKindTag {
#[must_use]
pub fn all() -> BTreeSet<EdgeKindTag> {
let mut s = BTreeSet::new();
s.insert(EdgeKindTag::Link);
s.insert(EdgeKindTag::SummaryInput);
s.insert(EdgeKindTag::ParentPartition);
s.insert(EdgeKindTag::PartitionContains);
s
}
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphEdge {
pub from: NodeRef,
pub to: NodeRef,
pub kind: EdgeKind,
}
#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
pub struct GraphNode {
pub r#ref: NodeRef,
}
#[derive(Debug, Clone, Default, Serialize, Deserialize)]
pub struct Graph {
pub nodes: Vec<GraphNode>,
pub edges: Vec<GraphEdge>,
}
#[non_exhaustive]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(default)]
pub struct TraverseOpts {
pub include_kinds: BTreeSet<EdgeKindTag>,
pub max_nodes: u32,
pub include_partition_edges: bool,
}
impl Default for TraverseOpts {
fn default() -> Self {
Self {
include_kinds: EdgeKindTag::all(),
max_nodes: 256,
include_partition_edges: true,
}
}
}
impl TraverseOpts {
#[must_use]
pub fn permits(&self, tag: EdgeKindTag) -> bool {
if !self.include_kinds.contains(&tag) {
return false;
}
if !self.include_partition_edges
&& matches!(
tag,
EdgeKindTag::ParentPartition | EdgeKindTag::PartitionContains
)
{
return false;
}
true
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn default_opts_permit_everything() {
let o = TraverseOpts::default();
assert!(o.permits(EdgeKindTag::Link));
assert!(o.permits(EdgeKindTag::SummaryInput));
assert!(o.permits(EdgeKindTag::ParentPartition));
assert!(o.permits(EdgeKindTag::PartitionContains));
}
#[test]
fn include_partition_edges_off_blocks_partition_tags() {
let o = TraverseOpts {
include_partition_edges: false,
..TraverseOpts::default()
};
assert!(o.permits(EdgeKindTag::Link));
assert!(!o.permits(EdgeKindTag::ParentPartition));
assert!(!o.permits(EdgeKindTag::PartitionContains));
}
#[test]
fn graph_serdes() {
let g = Graph::default();
let s = serde_json::to_string(&g).unwrap();
let _: Graph = serde_json::from_str(&s).unwrap();
}
}