use graph_mvcc::{Graph, IGraph, NodeId, TxError};
use std::collections::HashMap;
#[derive(Debug, Clone)]
enum NodeType {
City,
Airport,
TrainStation,
}
#[derive(Debug, Clone)]
enum EdgeType {
Road,
Rail,
AirToLondon, AirToTokyo,
AirToParis,
RailToParis,
RailToTokyo,
RoadToParis,
RoadToTokyo,
}
impl EdgeType {
fn as_string(&self) -> String {
match self {
EdgeType::Road => "Road".to_string(),
EdgeType::Rail => "Rail".to_string(),
EdgeType::AirToLondon => "AirToLondon".to_string(),
EdgeType::AirToTokyo => "AirToTokyo".to_string(),
EdgeType::AirToParis => "AirToParis".to_string(),
EdgeType::RailToParis => "RailToParis".to_string(),
EdgeType::RailToTokyo => "RailToTokyo".to_string(),
EdgeType::RoadToParis => "RoadToParis".to_string(),
EdgeType::RoadToTokyo => "RoadToTokyo".to_string(),
}
}
}
fn main() -> Result<(), TxError> {
println!("๐ MVCC Transportation Network Example");
println!("======================================");
let mut graph = Graph::new();
let mut node_names = HashMap::new();
let tx1 = graph.start_transaction();
println!("Started transaction {}", tx1.txid);
println!("\n๐ Creating Transportation Nodes...");
let nodes = create_transportation_nodes(&mut graph, &mut node_names)?;
println!("\n๐ฃ๏ธ Creating Transportation Connections...");
create_connections(&mut graph, &nodes, &node_names)?;
println!("\nโ ๏ธ Testing Collision Detection...");
test_collision_detection(&mut graph, &nodes)?;
println!("\n๐ Testing Concurrent Transactions & Read Locks...");
test_concurrent_transactions(&mut graph, &nodes)?;
println!("\n๐บ๏ธ Testing Graph Traversal...");
test_graph_traversal(&mut graph, &nodes, &node_names)?;
println!("\n๐ Transportation Network Example Complete!");
Ok(())
}
fn create_transportation_nodes(graph: &mut Graph, node_names: &mut HashMap<NodeId, String>) -> Result<Vec<NodeId>, TxError> {
let mut nodes = Vec::new();
let locations = vec![
("New York City", NodeType::City),
("London", NodeType::City),
("Tokyo", NodeType::City),
("Paris", NodeType::City),
("JFK Airport", NodeType::Airport),
("Heathrow Airport", NodeType::Airport),
("Narita Airport", NodeType::Airport),
("Charles de Gaulle Airport", NodeType::Airport),
("Penn Station NYC", NodeType::TrainStation),
("St Pancras London", NodeType::TrainStation),
("Tokyo Station", NodeType::TrainStation),
("Gare du Nord Paris", NodeType::TrainStation),
];
for (name, node_type) in locations {
let node_id = IGraph::add_node(graph, None)?;
node_names.insert(node_id.clone(), name.to_string());
println!(" โ Created {:?}: {} (ID: {:?})", node_type, name, node_id);
nodes.push(node_id);
}
Ok(nodes)
}
fn create_connections(graph: &mut Graph, nodes: &[NodeId], node_names: &HashMap<NodeId, String>) -> Result<(), TxError> {
if nodes.len() >= 12 {
println!(" โ๏ธ Creating Air Routes...");
IGraph::add_edge(graph, None, nodes[4].clone(), nodes[5].clone(), EdgeType::AirToLondon.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[4]),
get_node_name(node_names, &nodes[5]),
EdgeType::AirToLondon.as_string());
IGraph::add_edge(graph, None, nodes[4].clone(), nodes[6].clone(), EdgeType::AirToTokyo.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[4]),
get_node_name(node_names, &nodes[6]),
EdgeType::AirToTokyo.as_string());
IGraph::add_edge(graph, None, nodes[5].clone(), nodes[7].clone(), EdgeType::AirToParis.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[5]),
get_node_name(node_names, &nodes[7]),
EdgeType::AirToParis.as_string());
println!(" ๐ Creating Rail Routes...");
IGraph::add_edge(graph, None, nodes[8].clone(), nodes[9].clone(), EdgeType::RailToParis.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[8]),
get_node_name(node_names, &nodes[9]),
EdgeType::RailToParis.as_string());
IGraph::add_edge(graph, None, nodes[10].clone(), nodes[11].clone(), EdgeType::RailToTokyo.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[10]),
get_node_name(node_names, &nodes[11]),
EdgeType::RailToTokyo.as_string());
println!(" ๐ฃ๏ธ Creating Road Routes...");
IGraph::add_edge(graph, None, nodes[0].clone(), nodes[1].clone(), EdgeType::RoadToParis.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[0]),
get_node_name(node_names, &nodes[1]),
EdgeType::RoadToParis.as_string());
IGraph::add_edge(graph, None, nodes[1].clone(), nodes[2].clone(), EdgeType::RoadToTokyo.as_string())?;
println!(" {} โ {} ({})",
get_node_name(node_names, &nodes[1]),
get_node_name(node_names, &nodes[2]),
EdgeType::RoadToTokyo.as_string());
}
Ok(())
}
fn test_collision_detection(graph: &mut Graph, nodes: &[NodeId]) -> Result<(), TxError> {
if nodes.len() >= 12 {
println!(" ๐ Attempting to add duplicate AirToLondon connection...");
match IGraph::add_edge(graph, None, nodes[4].clone(), nodes[5].clone(), EdgeType::AirToLondon.as_string()) {
Err(TxError::Collision(msg)) => {
println!(" โ
Duplicate connection correctly rejected: {}", msg);
},
Ok(_) => {
println!(" โ Duplicate connection should have been rejected!");
},
Err(e) => {
println!(" โ Unexpected error: {:?}", e);
}
}
println!(" ๐ Attempting to add second air connection from same node (should fail)...");
match IGraph::add_edge(graph, None, nodes[4].clone(), nodes[7].clone(), EdgeType::AirToParis.as_string()) {
Err(TxError::Collision(msg)) => {
println!(" โ
Collision correctly detected: {}", msg);
},
Ok(_) => {
println!(" โ This should have triggered a collision (node already has AirToTokyo)!");
},
Err(e) => {
println!(" โ Unexpected error: {:?}", e);
}
}
println!(" ๐ Attempting to add different edge type (should succeed)...");
match IGraph::add_edge(graph, None, nodes[0].clone(), nodes[3].clone(), EdgeType::RoadToParis.as_string()) {
Ok(_) => {
println!(" โ
Different edge type added successfully (no collision)");
},
Err(e) => {
println!(" โ Different edge type should have succeeded: {:?}", e);
}
}
}
Ok(())
}
fn test_concurrent_transactions(graph: &mut Graph, nodes: &[NodeId]) -> Result<(), TxError> {
if nodes.len() >= 4 {
let tx1 = IGraph::start_transaction(graph);
let tx2 = IGraph::start_transaction(graph);
println!(" Started concurrent transactions: {} and {}", tx1.txid, tx2.txid);
println!(" ๐ Testing concurrent read operations...");
match IGraph::get_nodes(graph, Some(tx1.clone()), nodes[4].clone(), vec![EdgeType::AirToLondon.as_string()]) {
Ok(destinations) => {
println!(" Tx{}: Found {} AirToLondon destinations", tx1.txid, destinations.len());
},
Err(e) => {
println!(" Tx{}: Error during traversal: {:?}", tx1.txid, e);
}
}
match IGraph::get_nodes(graph, Some(tx2.clone()), nodes[0].clone(), vec![EdgeType::RoadToParis.as_string()]) {
Ok(destinations) => {
println!(" Tx{}: Found {} RoadToParis destinations", tx2.txid, destinations.len());
},
Err(e) => {
println!(" Tx{}: Error during traversal: {:?}", tx2.txid, e);
}
}
match IGraph::commit_transaction(graph, tx1.clone()) {
Ok(_) => println!(" โ
Transaction {} committed successfully", tx1.txid),
Err(e) => println!(" โ Transaction {} failed to commit: {:?}", tx1.txid, e),
}
match IGraph::commit_transaction(graph, tx2.clone()) {
Ok(_) => println!(" โ
Transaction {} committed successfully", tx2.txid),
Err(e) => println!(" โ Transaction {} failed to commit: {:?}", tx2.txid, e),
}
}
Ok(())
}
fn test_graph_traversal(graph: &mut Graph, nodes: &[NodeId], node_names: &HashMap<NodeId, String>) -> Result<(), TxError> {
if nodes.len() >= 12 {
println!(" ๐บ๏ธ Testing single-hop traversals...");
let air_destinations = IGraph::get_nodes(graph, None, nodes[4].clone(), vec![EdgeType::AirToLondon.as_string()])?;
println!(" โ๏ธ From {}: {} AirToLondon destinations found",
get_node_name(node_names, &nodes[4]), air_destinations.len());
let air_tokyo = IGraph::get_nodes(graph, None, nodes[4].clone(), vec![EdgeType::AirToTokyo.as_string()])?;
println!(" โ๏ธ From {}: {} AirToTokyo destinations found",
get_node_name(node_names, &nodes[4]), air_tokyo.len());
let road_destinations = IGraph::get_nodes(graph, None, nodes[0].clone(), vec![EdgeType::RoadToParis.as_string()])?;
println!(" ๐ฃ๏ธ From {}: {} RoadToParis destinations found",
get_node_name(node_names, &nodes[0]), road_destinations.len());
let rail_destinations = IGraph::get_nodes(graph, None, nodes[8].clone(), vec![EdgeType::RailToParis.as_string()])?;
println!(" ๐ From {}: {} RailToParis destinations found",
get_node_name(node_names, &nodes[8]), rail_destinations.len());
println!(" ๐ Testing multi-hop traversals...");
let multi_hop = IGraph::get_nodes(graph, None, nodes[4].clone(),
vec![EdgeType::AirToLondon.as_string(), EdgeType::AirToParis.as_string()])?;
println!(" โ๏ธโ๏ธ AirToLondonโAirToParis from {}: {} final destinations",
get_node_name(node_names, &nodes[4]), multi_hop.len());
println!(" โก Testing temporary transactions...");
let temp_destinations = IGraph::get_nodes(graph, None, nodes[1].clone(), vec![EdgeType::RoadToTokyo.as_string()])?;
println!(" ๐ Temporary transaction traversal: {} destinations found", temp_destinations.len());
}
Ok(())
}
fn get_node_name(node_names: &HashMap<NodeId, String>, node_id: &NodeId) -> String {
node_names.get(node_id)
.map(|s| s.clone())
.unwrap_or_else(|| format!("{:?}", node_id))
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_transportation_network_example() {
assert!(main().is_ok());
}
}