1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
//! Hyperedge fan representation.
//!
//! A [`Fan`] represents the instance-level realization of a schema
//! hyper-edge: a parent node connected to multiple child nodes via
//! labeled positions from the hyper-edge's signature.
use std::collections::HashMap;
use serde::{Deserialize, Serialize};
/// A hyperedge fan in a W-type instance.
///
/// Fans capture multi-way relationships. For example, a SQL foreign key
/// is a 4-ary hyper-edge connecting a table, a column, a referenced
/// table, and a referenced column.
#[derive(Clone, Debug, PartialEq, Eq, Serialize, Deserialize)]
pub struct Fan {
/// The schema hyper-edge this fan instantiates.
pub hyper_edge_id: String,
/// The parent node ID.
pub parent: u32,
/// Labeled child positions: label name to node ID.
pub children: HashMap<String, u32>,
}
impl Fan {
/// Create a new fan for the given hyper-edge.
#[must_use]
pub fn new(hyper_edge_id: impl Into<String>, parent: u32) -> Self {
Self {
hyper_edge_id: hyper_edge_id.into(),
parent,
children: HashMap::new(),
}
}
/// Add a labeled child to the fan.
#[must_use]
pub fn with_child(mut self, label: impl Into<String>, node_id: u32) -> Self {
self.children.insert(label.into(), node_id);
self
}
/// Returns the arity (number of children) of this fan.
#[must_use]
pub fn arity(&self) -> usize {
self.children.len()
}
/// Returns the node ID for the given label, if present.
#[must_use]
pub fn child(&self, label: &str) -> Option<u32> {
self.children.get(label).copied()
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn fan_construction() {
let fan = Fan::new("fk_posts_users", 0)
.with_child("table", 1)
.with_child("column", 2)
.with_child("ref_table", 3)
.with_child("ref_column", 4);
assert_eq!(fan.arity(), 4);
assert_eq!(fan.child("table"), Some(1));
assert_eq!(fan.child("column"), Some(2));
assert_eq!(fan.child("missing"), None);
}
}