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 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 801 802 803 804 805 806 807 808 809 810 811 812 813 814 815 816 817 818 819 820 821 822 823 824 825 826 827 828
//! TTGraph is:
//! + A container or database for many different data, which cross-reference each other, forming a graph-like data structure.
//! + **Typed graph:** A collection of multiple types of nodes. Each node hold some private data, and some pointers/edges/references to other nodes.
//! + **Transactional graph:** All operations on the graph are organized by transaction, which means an atomic group of operation is applied at the same time.
//!
//! TTGraph provides:
//! + A convinient container for different types of data, which provides some useful methods to deal with types.
//! + A data struct to maintain the connection between nodes. TTGraph create a reflection for all types to track the connection between nodes, named as *link*. This allows some fancy operations, such as redirect links and maintain bidirectional links.
//! + A clean interface to help get rid of some annoying compile errors. The design of transaction tries to prevent having a non-mutable reference and a mutable reference of the same object at the same time, and tries not to get into a maze of lifetimes.
//! + TTGraph is originally designed as an Intermediate Representation system for compilers, but its potential is not limited.
//!
//! TTGraph does **not** currently provides, but may be improved in the future:
//! + Very high performance. Though TTGraph operations are relatively cheap (mostly O(log(n))), it is not a high performance database.
//! + Very large capacity. All data are stored in memory.
//!
//! # Motivational Example
//!
//! ## Typed Node Declaration
//!
//! Assume there are a few factories, workers and products, the following example use TTGraph to maintain their data.
//!
//! ```
//! use ttgraph::*;
//! use std::collections::HashSet;
//!
//! #[derive(TypedNode)]
//! struct FactoryNode{
//! name: String,
//! workers: HashSet<NodeIndex>,
//! products: HashSet<NodeIndex>,
//! }
//!
//! #[derive(TypedNode)]
//! struct WorkerNode{
//! name: String,
//! factory: NodeIndex,
//! produced: Vec<NodeIndex>,
//! }
//!
//! #[derive(TypedNode)]
//! struct ProductNode{
//! id: usize
//! }
//! ```
//!
//! Here, a factory have a name, multiple workers and products. `name` is a **data field**, which TTGraph does not care about. It can be any type in Rust.
//!
//! `workers` and `products` are **links**. A link is a connection to another node. TTGraph use [`NodeIndex`] to index a node, which implements [`Copy`]. If field is one of the following types, it is treated as a link. (Note: types are matched by name in the macros, `ttgraph::NodeIndex`/`NodeIndex`/`std::collections::Vec::<NodeIndex>`/`Vec::<ttgraph::NodeIndex>` are all acceptable.)
//!
//! + Direct link: `NodeIndex`
//! + Vector link: `Vec<NodeIndex>`
//! + Unordered set link: `HashSet<NodeIndex>`
//! + Ordered set link: `BTreeSet<NodeIndex>`
//!
//! ## Graph and Transaction
//!
//! Next example shows how to build a graph.
//!
//! ```
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! // Use an node_enum to collect all node types together
//! node_enum!{
//! enum Node{
//! Factory(FactoryNode),
//! Worker(WorkerNode),
//! Product(ProductNode),
//! }
//! }
//!
//! # fn main() {
//! // Create the context
//! let ctx = Context::new();
//! // Create a graph of Node
//! let mut graph = Graph::<Node>::new(&ctx);
//!
//! // Does some initial operations with a transaction
//! // Actual type: Transaction::<Node>, <Node> can be inferenced when commited
//! let mut trans = Transaction::new(&ctx);
//! let product1 = trans.insert(Node::Product(ProductNode{ id: 1 }));
//! let product2 = trans.insert(Node::Product(ProductNode{ id: 2 }));
//! let worker1 = trans.alloc();
//! let worker2 = trans.alloc();
//! let factory = trans.insert(Node::Factory(FactoryNode{
//! name: "Factory".to_string(),
//! workers: HashSet::from([worker1, worker2]),
//! products: HashSet::from([product1, product2]),
//! }));
//! trans.fill_back(worker1, Node::Worker(WorkerNode{
//! name: "Alice".to_string(),
//! factory,
//! produced: vec![product2],
//! }));
//! trans.fill_back(worker2, Node::Worker(WorkerNode{
//! name: "Bob".to_string(),
//! factory,
//! produced: vec![product1],
//! }));
//!
//! // Commit the transaction to the graph
//! graph.commit(trans);
//!
//! // Get the factory node back
//! let factory_node = get_node!(graph, Node::Factory, factory).unwrap();
//! assert_eq!(factory_node.name, "Factory");
//! assert_eq!(factory_node.workers, HashSet::from([worker1, worker2]));
//! assert_eq!(factory_node.products, HashSet::from([product1, product2]));
//! # }
//! ```
//!
//! First, the [`node_enum!`] macro is used to create a enum to collect all types of nodes. It is a proc_macro instead of proc_macro_derive for extendable syntax in the latter examples. The enum inside of `node_enum!` will implements trait `NodeEnum` and can be used in `Graph`.
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! node_enum!{
//! enum Node{
//! Factory(FactoryNode),
//! Worker(WorkerNode),
//! Product(ProductNode),
//! }
//! }
//! # fn main() {}
//! ```
//!
//! Then, create a [`Context`] and a [`Graph`] using that context. The context is used to ensure the NodeIndexes are consistent across all transactions. Graph does not hold a reference to the context, so it is the user's reponsibility to keep it.
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! # node_enum!{
//! # enum Node{
//! # Factory(FactoryNode),
//! # Worker(WorkerNode),
//! # Product(ProductNode),
//! # }
//! # }
//! # fn main() {
//! let ctx = Context::new();
//! let mut graph = Graph::<Node>::new(&ctx);
//! # }
//! ```
//!
//! Next, a [`Transaction`] is created using the same context as the graph. After operations are done on the transcations, it can be committed to the graph with method [`commit`](Graph::commit). Transaction does not hold a reference to the graph and they have independent lifetime. (Though, it does nothing if a transaction outlives the graph)
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! # node_enum!{
//! # enum Node{
//! # Factory(FactoryNode),
//! # Worker(WorkerNode),
//! # Product(ProductNode),
//! # }
//! # }
//! # fn main() {
//! # let ctx = Context::new();
//! # let mut graph = Graph::<Node>::new(&ctx);
//! let mut trans = Transaction::new(&ctx);
//! // Do something with trans
//! graph.commit(trans);
//! # }
//! ```
//!
//! Now we take a closer look on how to build the graph. `Product` nodes are the simplest, it only have a id. Use [`insert`](Transaction::insert) to add a node into the transaction. It returns a [`NodeIndex`] pointing to the new node, which means later we can use `product1` and `product2` to retrieve the node from the graph.
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! # node_enum!{
//! # enum Node{
//! # Factory(FactoryNode),
//! # Worker(WorkerNode),
//! # Product(ProductNode),
//! # }
//! # }
//! # fn main() {
//! # let ctx = Context::new();
//! # let mut graph = Graph::<Node>::new(&ctx);
//! # let mut trans = Transaction::new(&ctx);
//! let product1 = trans.insert(Node::Product(ProductNode{ id: 1 }));
//! let product2 = trans.insert(Node::Product(ProductNode{ id: 2 }));
//! # graph.commit(trans);
//! # }
//! ```
//!
//! Factories and workers have a more complex relationship, as they cross-refenerence each other. That means we cannot make a `FactoryNode` or a `WorkerNode` alone. Lucky, TTGraph does operations in transaction, we can first allocate a [`NodeIndex`] for the workers with method [`alloc`](Transaction::alloc), then fill the data back with method [`fill_back`](Transaction::fill_back). The transaction prevents dangling [`NodeIndex`] by checking all allocated nodes are filled back when committed.
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! # node_enum!{
//! # enum Node{
//! # Factory(FactoryNode),
//! # Worker(WorkerNode),
//! # Product(ProductNode),
//! # }
//! # }
//! # fn main() {
//! # let ctx = Context::new();
//! # let mut graph = Graph::<Node>::new(&ctx);
//! # let mut trans = Transaction::new(&ctx);
//! # let product1 = trans.insert(Node::Product(ProductNode{ id: 1 }));
//! # let product2 = trans.insert(Node::Product(ProductNode{ id: 2 }));
//! let worker1 = trans.alloc();
//! let worker2 = trans.alloc();
//! let factory = trans.insert(Node::Factory(FactoryNode{
//! name: "Factory".to_string(),
//! workers: HashSet::from([worker1, worker2]),
//! products: HashSet::from([product1, product2]),
//! }));
//! trans.fill_back(worker1, Node::Worker(WorkerNode{
//! name: "Alice".to_string(),
//! factory,
//! produced: vec![product2],
//! }));
//! trans.fill_back(worker2, Node::Worker(WorkerNode{
//! name: "Bob".to_string(),
//! factory,
//! produced: vec![product1],
//! }));
//! # graph.commit(trans);
//! # }
//! ```
//!
//! Finally, after committing the transaction to the graph, we have a graph with the nodes described above. We can use [`NodeIndex`] to get the node back. [`get_node!`] macro is used when the type of the node is previously known, which returns an `Option<&TypedNode>` to indicate if the node is avaiable.
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! # node_enum!{
//! # enum Node{
//! # Factory(FactoryNode),
//! # Worker(WorkerNode),
//! # Product(ProductNode),
//! # }
//! # }
//! # fn main() {
//! # let ctx = Context::new();
//! # let mut graph = Graph::<Node>::new(&ctx);
//! # let mut trans = Transaction::new(&ctx);
//! # let product1 = trans.insert(Node::Product(ProductNode{ id: 1 }));
//! # let product2 = trans.insert(Node::Product(ProductNode{ id: 2 }));
//! # let worker1 = trans.alloc();
//! # let worker2 = trans.alloc();
//! # let factory = trans.insert(Node::Factory(FactoryNode{
//! # name: "Factory".to_string(),
//! # workers: HashSet::from([worker1, worker2]),
//! # products: HashSet::from([product1, product2]),
//! # }));
//! # trans.fill_back(worker1, Node::Worker(WorkerNode{
//! # name: "Alice".to_string(),
//! # factory,
//! # produced: vec![product2],
//! # }));
//! # trans.fill_back(worker2, Node::Worker(WorkerNode{
//! # name: "Bob".to_string(),
//! # factory,
//! # produced: vec![product1],
//! # }));
//! # graph.commit(trans);
//! let factory_node = get_node!(graph, Node::Factory, factory).unwrap();
//! assert_eq!(factory_node.name, "Factory");
//! assert_eq!(factory_node.workers, HashSet::from([worker1, worker2]));
//! assert_eq!(factory_node.products, HashSet::from([product1, product2]));
//! # }
//! ```
//!
//! For more operations, please view the documents on struct [`Graph`] and [`Transaction`].
//!
//! ## Bidiretional links
//!
//! TTGraph supports bidirectional link declaration. In this example, the `workers` field of `Factory` and the `factory` field of `Worker` is in fact a pair of bidirectional link. We can modify the [`node_enum!`] declaration for more supports.
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::HashSet;
//! # #[derive(TypedNode)]
//! # struct FactoryNode{
//! # name: String,
//! # workers: HashSet<NodeIndex>,
//! # products: HashSet<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct WorkerNode{
//! # name: String,
//! # factory: NodeIndex,
//! # produced: Vec<NodeIndex>,
//! # }
//! # #[derive(TypedNode)]
//! # struct ProductNode{
//! # id: usize
//! # }
//! node_enum!{
//! enum Node{
//! Factory(FactoryNode),
//! Worker(WorkerNode),
//! Product(ProductNode),
//! }
//! bidirectional!{
//! Factory.workers <-> Worker.factory,
//! }
//! }
//!
//! # fn main() {
//! let ctx = Context::new();
//! let mut graph = Graph::<Node>::new(&ctx);
//!
//! let mut trans = Transaction::new(&ctx);
//! let product1 = trans.insert(Node::Product(ProductNode{ id: 1 }));
//! let product2 = trans.insert(Node::Product(ProductNode{ id: 2 }));
//! let factory = trans.insert(Node::Factory(FactoryNode{
//! name: "Factory".to_string(),
//! // Here we leave this set empty to demonstrate it can be automatically filled
//! workers: HashSet::new(),
//! products: HashSet::from([product1, product2]),
//! }));
//! let worker1 = trans.insert(Node::Worker(WorkerNode{
//! name: "Alice".to_string(),
//! factory,
//! produced: vec![product2],
//! }));
//! let worker2 = trans.insert(Node::Worker(WorkerNode{
//! name: "Bob".to_string(),
//! factory,
//! produced: vec![product1],
//! }));
//!
//! graph.commit(trans);
//!
//! // Get the factory node back
//! let factory_node = get_node!(graph, Node::Factory, factory).unwrap();
//! assert_eq!(factory_node.name, "Factory");
//! assert_eq!(factory_node.workers, HashSet::from([worker1, worker2]));
//! assert_eq!(factory_node.products, HashSet::from([product1, product2]));
//! # }
//! ```
//!
//! Here, the `bidiretional!` macro inside of [`node_enum!`] macro is used to declare bidirecitonal links.
//!
//! + Use `variant.field <-> variant.field,` to indicate a pair of bidirecitonal links. Note: variant of the enum, not type!
//! + `bidiretional!` is not actually a macro, it can only be used inside of `node_enum!`
//!
//! Next, when making the factory node, its workers are simply left empty. However, after commited to the graph, TTGraph automatically adds the bidirectional links into it.
//!
//! Rules of bidiretional links are:
//!
//! + Bidirectional links may be formed between: a pair of `NodeIndex`, between `NodeIndex` and `Set<NodeIndex>`, a pair of `Set<NodeIndex>`. (`Set` may be `HashSet` or `BTreeSet`, `Vec` is not supported currently)
//! + When a link is added, the opposite side of the bidiretional link is checked. If the bidiretional link is already there, nothing happens. If that link have a place to be added, it is automatially added. Otherwise, it panics for conflict.
//! + When a link is removed, the opposite side of the bidiretional link is checked. If the bidiretional link is there, it is removed. Otherwise, since TTGraph does not know if the user removes it on purpose, it is assumed that nothing should happen.
//! + `NodeIndex` field: link can be added if it is [`NodeIndex::empty()`], otherwise it conflicts and panics. Link can be removed if it is not empty, but does not panic if it is.
//! + `Set<NodeIndex>` field: link can always be added into or removed from the set.
//! + When modifying existing pairs of bidiretional links, ensure the modification happens in the same transaction to prevent conflict. TTGraph does all other operations before maintaining bidiretional links.
//!
//! ## Get data by name and group
//!
//! TTGraph supports few operations for type erasure, targeting cases that some typed nodes have some similar fields, and matching the enum for these field is verbose.
//!
//! Following last example, assume there are two types of workers, robots and humans. They may have very different data, but they both have a name. Now we want to make a name list for all the workers. Typical solution is to match the NodeEnum, but TTGraph gives another solution by getting data by name.
//!
//! [`data_ref_by_name`](NodeEnum::data_ref_by_name) method provides an interface to access a data field by its name and type. If the node have that field and the type matches (through `std::any::Any::downcast_ref`), `Some(&Type)` is returned, otherwise `None` is returned.
//!
//! ```rust
//! # use ttgraph::*;
//! #[derive(TypedNode)]
//! struct HumanWorkerNode{
//! name: String,
//! // ... other data
//! }
//! #[derive(TypedNode)]
//! struct RobotWorkerNode{
//! name: String,
//! // ... other data
//! }
//!
//! node_enum!{
//! enum Node{
//! Human(HumanWorkerNode),
//! Robot(RobotWorkerNode),
//! // ... other nodes
//! }
//! }
//!
//! # fn main() {
//! let ctx = Context::new();
//! let mut graph = Graph::<Node>::new(&ctx);
//! # let mut trans = Transaction::new(&ctx);
//! # let idx = trans.insert(Node::Human(HumanWorkerNode{name: "human".to_string()}));
//! # graph.commit(trans);
//! // ... building the graph
//!
//! // idx: NodeIndex, node: &Node
//! let node = graph.get(idx).unwrap();
//!
//! // Not so convinient way to get the name
//! let name = match node {
//! Node::Human(human) => Some(&human.name),
//! Node::Robot(robot) => Some(&robot.name),
//! _ => None,
//! };
//!
//! // A simplified solution
//! // Here, "name" is the field's name
//! // The "name" field is a String, so this variable is an Option<&str>
//! let name = node.data_ref_by_name::<String>("name");
//! # }
//! ```
//!
//! Further more, if we want to iterate all workers, skipping all the other nodes, the grouping mechanism in TTGraph can come to use.
//!
//! Here, the two variant `Human` and `Robot` is in the `worker` group. Use the [`iter_group`](Graph::iter_group) method to iterate all nodes within the group.
//!
//! Notes:
//!
//! + Variants can be inside of multiple or none groups.
//! + Currently, this method does not provide performance enhancement, as it is only a wrapper on matching the variants according to the group name.
//!
//! ```rust
//! # use ttgraph::*;
//! # #[derive(TypedNode)]
//! # struct HumanWorkerNode{
//! # name: String,
//! # // ... other data
//! # }
//! # #[derive(TypedNode)]
//! # struct RobotWorkerNode{
//! # name: String,
//! # // ... other data
//! # }
//! node_enum!{
//! enum Node{
//! Human(HumanWorkerNode),
//! Robot(RobotWorkerNode),
//! // ... other nodes
//! }
//! group!{
//! worker{Human, Robot},
//! }
//! }
//!
//! # fn main() {
//! # let ctx = Context::new();
//! # let graph = Graph::<Node>::new(&ctx);
//! for (idx, node) in graph.iter_group("worker") {
//! let name = node.data_ref_by_name::<String>("name").unwrap();
//! // ...
//! }
//! # }
//! ```
//!
//! Links may be grouped too. Assume workers may produce different kinds of products, and make them into a `product` group can help iterate through all of them.
//!
//! Notes:
//! + A link field can be inside multiple or none groups. Syntax: `#[group(group1, group2, ...)]`
//! + Yes, its form is inconsitent with [`node_enum!`]. The problem is if a struct is inside a macro, the linter (rust-analyzer) fails to show its content. The author personally thinks the `group!` form is clearer, but does not worth ruining the linter.
//!
//! ```rust
//! # use ttgraph::*;
//! # use std::collections::BTreeSet;
//! #[derive(TypedNode)]
//! struct HumanWorkerNode{
//! name: String,
//! #[group(product)]
//! cooked: BTreeSet<NodeIndex>,
//! #[group(product)]
//! maked: BTreeSet<NodeIndex>,
//! // ... other data
//! }
//! #[derive(TypedNode)]
//! struct RobotWorkerNode{
//! name: String,
//! #[group(product)]
//! manufactured: BTreeSet<NodeIndex>,
//! // ... other data
//! }
//! # node_enum!{
//! # enum Node{
//! # Human(HumanWorkerNode),
//! # Robot(RobotWorkerNode),
//! # // ... other nodes
//! # }
//! # }
//! # fn main() {
//! # let ctx = Context::new();
//! # let mut graph = Graph::<Node>::new(&ctx);
//! # let mut trans = Transaction::new(&ctx);
//! # let idx = trans.insert(Node::Robot(RobotWorkerNode{name: "robot".to_string(), manufactured: BTreeSet::new()}));
//! # graph.commit(trans);
//!
//! let node = graph.get(idx).unwrap();
//! for idx in node.get_links_by_group("product") {
//! // Now idx binds to all NodeIndex inside the product group
//! }
//! # }
//! ```
//!
//! Other methods for type erasure are listed in the document of `NodeEnum` and `TypedNode` traits.
//!
//! ## Link type check
//!
//! A node links to other node with a [`NodeIndex`] in TTGraph, which is in fact weak typed as any variant in the node enum can be pointed by the NodeIndex.
//!
//! For debug reason, an optional link type check can be added with `link_type!{ #var.#field : #var, ... }`. When a transaction is committed, all changes which be checked. Panics if a NodeIndex points to the wrong enum variant.
//!
//! Feature `debug` is required. Otherwise all checks are skipped.
//!
//! ```rust
//! use ttgraph::*;
//! use std::collections::HashSet;
//! #[derive(TypedNode)]
//! struct FactoryNode{
//! name: String,
//! workers: HashSet<NodeIndex>,
//! }
//! #[derive(TypedNode)]
//! struct HumanWorkerNode{
//! name: String,
//! factory: NodeIndex,
//! }
//! #[derive(TypedNode)]
//! struct RobotWorkerNode{
//! name: String,
//! factory: NodeIndex,
//! }
//! node_enum!{
//! enum Node{
//! Factory(FactoryNode),
//! Human(HumanWorkerNode),
//! Robot(RobotWorkerNode),
//! }
//! link_type!{
//! Factory.workers : {Human, Robot},
//! Human.factory: Factory,
//! Robot.factory: Factory,
//! }
//! }
//! # fn main() {}
//! ```
//!
//! In this example, workers of a factory can link to human or robot, while the factory field of human and robot must link to a factory.
//!
//! ## Use group in `link_type!` and `bidirectional!`
//!
//! Groups can be used in `link_type!` and `bidirectional!`. To avoid confliction, group name should not be variant name in NodeEnum or link name in TypedNode.
//!
//! All `VarGroup.LinkGroup` will be expaneded into multiple `Var.Link` pairs of the group.
//!
//! The purpose of this feature is to greatly reduce the number of lines to describe link types and bidirectional links, especially in complex graph.
//!
//! If there are n types of the same type group and m links of the same link group, then one line of such description can replace n*m lines of trival description. (In bidirecitonal link description such line number is further squared)
//!
//! Here is a sophisticated example for to explain what this feature does.
//!
//! ```rust
//! # use ttgraph::*;
//! #[derive(TypedNode, Debug)]
//! struct Left1 {
//! #[group(l1)]
//! g1: NodeIndex,
//! #[group(l2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Left2 {
//! #[group(l1)]
//! g1: NodeIndex,
//! #[group(l2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Left3 {
//! #[group(l1)]
//! g1: NodeIndex,
//! #[group(l2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Left4 {
//! #[group(l1)]
//! g1: NodeIndex,
//! #[group(l2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Right1 {
//! #[group(r1)]
//! g1: NodeIndex,
//! #[group(r2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Right2 {
//! #[group(r1)]
//! g1: NodeIndex,
//! #[group(r2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Right3 {
//! #[group(r1)]
//! g1: NodeIndex,
//! #[group(r2)]
//! g2: NodeIndex,
//! }
//!
//! #[derive(TypedNode, Debug)]
//! struct Right4 {
//! #[group(r1)]
//! g1: NodeIndex,
//! #[group(r2)]
//! g2: NodeIndex,
//! }
//!
//! node_enum!{
//! enum LR{
//! Left1(Left1),
//! Left2(Left2),
//! Left3(Left3),
//! Left4(Left4),
//! Right1(Right1),
//! Right2(Right2),
//! Right3(Right3),
//! Right4(Right4),
//! }
//! group!{
//! left { Left1, Left2, Left3, Left4},
//! right { Right1, Right2, Right3, Right4},
//! LU {Left1, Left2},
//! LD {Left3, Left4},
//! RU {Right1, Right2},
//! RD {Right3, Right4},
//! }
//! link_type!{
//! left.l1: RU,
//! left.l2: RD,
//! right.r1: LU,
//! right.r2: LD,
//! }
//! bidirectional!{
//! LU.l1 <-> RU.r1,
//! LD.l1 <-> RU.r2,
//! LU.l2 <-> RD.r1,
//! LD.l2 <-> RD.r2,
//! }
//! }
//!
//! # fn main() {
//! let ctx = Context::new();
//! let mut graph = Graph::<LR>::new(&ctx);
//! let mut trans = Transaction::new(&ctx);
//!
//! let l1 = trans.alloc();
//! let l2 = trans.alloc();
//! let l3 = trans.alloc();
//! let l4 = trans.alloc();
//! let r1 = trans.alloc();
//! let r2 = trans.alloc();
//! let r3 = trans.alloc();
//! let r4 = trans.alloc();
//!
//! trans.fill_back(l1, LR::Left1(Left1 { g1: r1, g2: r3 }));
//! trans.fill_back(l2, LR::Left2(Left2 { g1: r2, g2: r4 }));
//! trans.fill_back(l3, LR::Left3(Left3 { g1: r1, g2: r4 }));
//! trans.fill_back(l4, LR::Left4(Left4 { g1: r2, g2: r3 }));
//!
//! trans.fill_back(r1, LR::Right1(Right1 { g1: NodeIndex::empty(), g2: NodeIndex::empty() }));
//! trans.fill_back(r2, LR::Right2(Right2 { g1: NodeIndex::empty(), g2: NodeIndex::empty() }));
//! trans.fill_back(r3, LR::Right3(Right3 { g1: NodeIndex::empty(), g2: NodeIndex::empty() }));
//! trans.fill_back(r4, LR::Right4(Right4 { g1: NodeIndex::empty(), g2: NodeIndex::empty() }));
//!
//! graph.commit(trans);
//!
//! let node = get_node!(graph, LR::Left1, l1).unwrap();
//! assert_eq!(node.g1, r1);
//! assert_eq!(node.g2, r3);
//! let node = get_node!(graph, LR::Left2, l2).unwrap();
//! assert_eq!(node.g1, r2);
//! assert_eq!(node.g2, r4);
//! let node = get_node!(graph, LR::Left3, l3).unwrap();
//! assert_eq!(node.g1, r1);
//! assert_eq!(node.g2, r4);
//! let node = get_node!(graph, LR::Left4, l4).unwrap();
//! assert_eq!(node.g1, r2);
//! assert_eq!(node.g2, r3);
//!
//! let node = get_node!(graph, LR::Right1, r1).unwrap();
//! assert_eq!(node.g1, l1);
//! assert_eq!(node.g2, l3);
//! let node = get_node!(graph, LR::Right2, r2).unwrap();
//! assert_eq!(node.g1, l2);
//! assert_eq!(node.g2, l4);
//! let node = get_node!(graph, LR::Right3, r3).unwrap();
//! assert_eq!(node.g1, l1);
//! assert_eq!(node.g2, l4);
//! let node = get_node!(graph, LR::Right4, r4).unwrap();
//! assert_eq!(node.g1, l2);
//! assert_eq!(node.g2, l3);
//! # }
//! ```
//!
//! ## Working In Progress
//!
//! + Graph creation macro. A sub-language to simplify great amount of `alloc_node`, `fill_back_node` and `new_node` calls.
//! + Graph transition. A way to conviently transit `Graph<NodeEnumA>` to `Graph<NodeEnumB>`, if `NodeEnumA` and `NodeEnumB` have a lot of common variants.
//! + Check when commit. A way to add runtime check when commit.
pub mod arena;
// pub mod graph;
mod typed_graph;
pub use typed_graph::*;