dlin-cli 0.2.0b1__tar.gz → 0.2.0b2__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (116) hide show
  1. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/Cargo.lock +4 -4
  2. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/Cargo.toml +3 -3
  3. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/PKG-INFO +1 -1
  4. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin/Cargo.toml +1 -0
  5. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin/src/cli.rs +45 -2
  6. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin/src/main.rs +14 -0
  7. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/tests/core.rs +13 -0
  8. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/tests/mod.rs +23 -0
  9. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/filter.rs +160 -0
  10. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/manifest.rs +79 -4
  11. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/column_graph.rs +137 -8
  12. dlin_cli-0.2.0b2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_non_direct_intermediate.snap +6 -0
  13. dlin_cli-0.2.0b2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_model_path_non_direct_annotation.snap +6 -0
  14. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/LICENSE +0 -0
  15. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/README.md +0 -0
  16. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin/README.md +0 -0
  17. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin/tests/integration_test.rs +0 -0
  18. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/Cargo.toml +0 -0
  19. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/README.md +0 -0
  20. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/error.rs +0 -0
  21. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/builder.rs +0 -0
  22. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/cache.rs +0 -0
  23. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/cross_model.rs +0 -0
  24. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/impact.rs +0 -0
  25. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/mod.rs +0 -0
  26. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/schema.rs +0 -0
  27. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/single_model.rs +0 -0
  28. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/tests/cache.rs +0 -0
  29. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/tests/impact.rs +0 -0
  30. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/column_lineage/types.rs +0 -0
  31. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/impact.rs +0 -0
  32. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/mod.rs +0 -0
  33. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot.snap +0 -0
  34. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_bfs_pseudoendpoint.snap +0 -0
  35. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_endpoints_fan_out.snap +0 -0
  36. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_endpoints_leaf_model.snap +0 -0
  37. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_multiple_focus_models.snap +0 -0
  38. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_no_source_exposure.snap +0 -0
  39. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_preserve_focus.snap +0 -0
  40. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_node_type_filter.snap +0 -0
  41. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_select_filter.snap +0 -0
  42. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_select_with_node_type.snap +0 -0
  43. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/graph/types.rs +0 -0
  44. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/input.rs +0 -0
  45. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/lib.rs +0 -0
  46. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/cache.rs +0 -0
  47. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/columns.rs +0 -0
  48. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/discovery.rs +0 -0
  49. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/jinja.rs +0 -0
  50. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/mod.rs +0 -0
  51. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/project.rs +0 -0
  52. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/sql.rs +0 -0
  53. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/parser/yaml_schema.rs +0 -0
  54. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/ascii.rs +0 -0
  55. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/dot.rs +0 -0
  56. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/html.rs +0 -0
  57. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/impact.rs +0 -0
  58. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/json.rs +0 -0
  59. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/layout.rs +0 -0
  60. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/list.rs +0 -0
  61. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/mermaid.rs +0 -0
  62. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/mod.rs +0 -0
  63. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/plain.rs +0 -0
  64. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_all_transformation_types.snap +0 -0
  65. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_escapes_special_chars.snap +0 -0
  66. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_id_collision_avoided.snap +0 -0
  67. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_impact_indirect.snap +0 -0
  68. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_impact_single.snap +0 -0
  69. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_single_model.snap +0 -0
  70. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__dot_via_path.snap +0 -0
  71. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid.snap +0 -0
  72. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid_indirect_edge_label.snap +0 -0
  73. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain.snap +0 -0
  74. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_multi_hop.snap +0 -0
  75. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_dotted_table_name.snap +0 -0
  76. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_id_collision_avoided.snap +0 -0
  77. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_label_escaping.snap +0 -0
  78. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_single_model.snap +0 -0
  79. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_no_sources.snap +0 -0
  80. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_single_model.snap +0 -0
  81. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__group_by_node_type.snap +0 -0
  82. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_all_edge_types.snap +0 -0
  83. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb.snap +0 -0
  84. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb_grouped.snap +0 -0
  85. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_group_by_directory.snap +0 -0
  86. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_lineage.snap +0 -0
  87. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_transitive_edges.snap +0 -0
  88. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__html__tests__snapshot_html_json.snap +0 -0
  89. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json.snap +0 -0
  90. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json_with_sql.snap +0 -0
  91. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text.snap +0 -0
  92. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text_with_sql.snap +0 -0
  93. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_json_with_sql.snap +0 -0
  94. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_lineage.snap +0 -0
  95. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_node_metadata.snap +0 -0
  96. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_json.snap +0 -0
  97. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_plain.snap +0 -0
  98. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__group_by_node_type.snap +0 -0
  99. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__mixed_direct_and_transitive_edges.snap +0 -0
  100. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_escapes_quotes.snap +0 -0
  101. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_lineage.snap +0 -0
  102. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_single_model.snap +0 -0
  103. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_collapse.snap +0 -0
  104. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_grouping.snap +0 -0
  105. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb.snap +0 -0
  106. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb_grouped.snap +0 -0
  107. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_group_by_directory.snap +0 -0
  108. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_lineage.snap +0 -0
  109. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__transitive_edge_rendering.snap +0 -0
  110. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__plain__tests__snapshot_plain.snap +0 -0
  111. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_json.snap +0 -0
  112. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_text.snap +0 -0
  113. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/summary.rs +0 -0
  114. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/src/render/svg.rs +0 -0
  115. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/crates/dlin-core/tests/column_lineage_test.rs +0 -0
  116. {dlin_cli-0.2.0b1 → dlin_cli-0.2.0b2}/pyproject.toml +0 -0
@@ -231,7 +231,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
231
231
 
232
232
  [[package]]
233
233
  name = "dlin"
234
- version = "0.2.0-beta.1"
234
+ version = "0.2.0-beta.2"
235
235
  dependencies = [
236
236
  "anyhow",
237
237
  "clap",
@@ -247,7 +247,7 @@ dependencies = [
247
247
 
248
248
  [[package]]
249
249
  name = "dlin-core"
250
- version = "0.2.0-beta.1"
250
+ version = "0.2.0-beta.2"
251
251
  dependencies = [
252
252
  "anyhow",
253
253
  "clap",
@@ -617,9 +617,9 @@ checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd"
617
617
 
618
618
  [[package]]
619
619
  name = "polyglot-sql"
620
- version = "0.4.1"
620
+ version = "0.4.2"
621
621
  source = "registry+https://github.com/rust-lang/crates.io-index"
622
- checksum = "59d58a3d9f8c43ce5549db154d456a8440eda074b888f1357ab775bc637a365e"
622
+ checksum = "3e264f8907f3a2050232cd06503afb637525038ddd388a2c9e03424e336cd9c8"
623
623
  dependencies = [
624
624
  "serde",
625
625
  "serde_json",
@@ -3,7 +3,7 @@ members = ["crates/*"]
3
3
  resolver = "3"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.0-beta.1"
6
+ version = "0.2.0-beta.2"
7
7
  edition = "2024"
8
8
  license = "MIT"
9
9
  repository = "https://github.com/eitsupi/dlin"
@@ -23,7 +23,7 @@ indexmap = "2"
23
23
  minijinja = "2"
24
24
  rayon = "1"
25
25
  globset = "0.4"
26
- polyglot-sql = { version = "0.4.1", default-features = false, features = ["all-dialects", "semantic"] }
26
+ polyglot-sql = { version = "0.4.2", default-features = false, features = ["all-dialects", "semantic"] }
27
27
  path-slash = "0.2.1"
28
28
  clap = { version = "4", features = ["derive", "env"] }
29
29
  libc = "0.2"
@@ -34,7 +34,7 @@ insta = "1"
34
34
  serial_test = "3.4.0"
35
35
 
36
36
  # internal
37
- dlin-core = { version = "0.2.0-beta.1", path = "crates/dlin-core" }
37
+ dlin-core = { version = "0.2.0-beta.2", path = "crates/dlin-core" }
38
38
 
39
39
  [workspace.lints.rust]
40
40
  unexpected_cfgs = { level = "warn", check-cfg = ['cfg(tarpaulin_include)'] }
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: dlin-cli
3
- Version: 0.2.0b1
3
+ Version: 0.2.0b2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -18,6 +18,7 @@ path = "src/main.rs"
18
18
  dlin-core = { workspace = true, features = ["clap", "column-lineage"] }
19
19
  clap = { workspace = true }
20
20
  anyhow = { workspace = true }
21
+ regex = { workspace = true }
21
22
  serde_json = { workspace = true }
22
23
  path-slash = { workspace = true }
23
24
  polyglot-sql = { workspace = true }
@@ -49,6 +49,8 @@ Examples:
49
49
  dlin graph orders -u 2 -d 1 # orders with 2 upstream, 1 downstream
50
50
  dlin graph -o json --json-full # JSON with all fields
51
51
  dlin list -o json # List all node names as JSON
52
+ dlin list orders -o json --json-full # Model details: path, description, columns, materialization
53
+ dlin list --search shipping # Search models by name or description (regex)
52
54
  dlin list orders stg_orders -o json # List specific models as JSON
53
55
  dlin impact orders -o json # Downstream impact analysis
54
56
  dlin summary # Project overview (node counts, etc.)
@@ -802,7 +804,9 @@ Output format (-o/--output):
802
804
  conditional CASE WHEN expression
803
805
  unknown could not classify
804
806
  sources[]
805
- table source model or raw table name
807
+ table source model or raw table name; empty string (\"\") when the
808
+ value originates from a literal (NULL, constant, UNNEST, etc.)
809
+ — rendered as \"(literal)\" in plain/mermaid/dot outputs
806
810
  column source column name
807
811
  model_path[] intermediate [model, column, transformation] triples traversed (omitted if empty)
808
812
  errors[] parse or resolution errors (non-empty → exit code 1)
@@ -1081,6 +1085,13 @@ Examples:
1081
1085
  # List models tagged 'finance'
1082
1086
  dlin list -s tag:finance
1083
1087
 
1088
+ # Search models by name or description (regex, case-insensitive)
1089
+ dlin list --search shipping
1090
+ dlin list --search '^stg_' # models whose name starts with stg_
1091
+ dlin list --search 'order|payment' # OR: name/description contains either
1092
+ dlin list --search staging --search customer # AND: must match both patterns
1093
+ dlin list --search shipping -o json --json-full
1094
+
1084
1095
  # Count models (combine with standard tools)
1085
1096
  dlin list --node-type model | wc -l
1086
1097
 
@@ -1088,7 +1099,13 @@ Examples:
1088
1099
  dlin impact orders -o json | jq -r '.[].impacted_nodes[].unique_id' | dlin list -o json --json-fields unique_id,sql_content
1089
1100
 
1090
1101
  # List models from changed files
1091
- git diff --name-only main | dlin list -o json --json-fields unique_id,label"
1102
+ git diff --name-only main | dlin list -o json --json-fields unique_id,label
1103
+
1104
+ # Find models that expose a specific column name (jq)
1105
+ dlin list -o json --json-full | jq '.[] | select(any(.columns[]; . == \"order_id\"))'
1106
+
1107
+ # Find models whose column name partially matches (jq)
1108
+ dlin list -o json --json-full | jq '.[] | select(any(.columns[]; contains(\"_amount\")))' "
1092
1109
  )]
1093
1110
  pub struct ListArgs {
1094
1111
  /// Model names to list (lists all nodes if omitted)
@@ -1128,6 +1145,32 @@ All selectors support glob patterns (*, **, ?, []):
1128
1145
  )]
1129
1146
  pub select: Option<String>,
1130
1147
 
1148
+ /// Filter nodes by name or description using a regex pattern (case-insensitive; repeatable for AND)
1149
+ #[arg(
1150
+ long,
1151
+ value_name = "PATTERN",
1152
+ action = clap::ArgAction::Append,
1153
+ long_help = "\
1154
+ Filter nodes whose name or description matches the regex pattern (case-insensitive).
1155
+
1156
+ Matching rules:
1157
+ - Tested against model name and description field (OR within a single pattern)
1158
+ - Plain text works as a simple substring match
1159
+ - Repeating --search applies AND logic: all patterns must match
1160
+
1161
+ Regex syntax (Rust regex, https://docs.rs/regex):
1162
+ order|payment match nodes containing 'order' or 'payment'
1163
+ ^stg_ match nodes whose name starts with 'stg_'
1164
+ (?-i)Order opt out of case-insensitivity for this pattern
1165
+
1166
+ Examples:
1167
+ dlin list --search shipping
1168
+ dlin list --search '^stg_'
1169
+ dlin list --search 'order|payment'
1170
+ dlin list --search staging --search customer # AND: must match both"
1171
+ )]
1172
+ pub search: Vec<String>,
1173
+
1131
1174
  /// Filter output by node type (comma-separated)
1132
1175
  #[arg(
1133
1176
  long = "node-type",
@@ -5,6 +5,7 @@ use anyhow::Result;
5
5
  use clap::Parser;
6
6
  use path_slash::PathExt as _;
7
7
  use polyglot_sql::{DialectType, Schema as _};
8
+ use regex::RegexBuilder;
8
9
 
9
10
  mod cli;
10
11
 
@@ -360,6 +361,19 @@ fn run_list_command(args: ListArgs) -> Result<()> {
360
361
  );
361
362
  }
362
363
  let filtered = graph::filter::filter_output_node_types(&filtered, &type_names, false);
364
+
365
+ // Compile search patterns and apply filter (AND across all patterns)
366
+ let search_patterns = args
367
+ .search
368
+ .iter()
369
+ .map(|p| {
370
+ RegexBuilder::new(p)
371
+ .case_insensitive(true)
372
+ .build()
373
+ .map_err(|e| anyhow::anyhow!("invalid --search pattern {:?}: {}", p, e))
374
+ })
375
+ .collect::<Result<Vec<_>>>()?;
376
+ let filtered = graph::filter::filter_by_search(&filtered, &search_patterns);
363
377
  warn_sql_mode_test_limitation(
364
378
  &args.source,
365
379
  filtered
@@ -971,6 +971,7 @@ fn test_cte_alias_resolution() {
971
971
  resource_type: "source".to_string(),
972
972
  description: None,
973
973
  path: None,
974
+ original_file_path: None,
974
975
  columns: src_cols,
975
976
  database: None,
976
977
  schema: None,
@@ -1000,6 +1001,7 @@ fn test_cte_alias_resolution() {
1000
1001
  config: ManifestConfig::default(),
1001
1002
  description: None,
1002
1003
  path: None,
1004
+ original_file_path: None,
1003
1005
  columns: stg_cols,
1004
1006
  compiled_code: Some("select id as item_id, name, status from items".to_string()),
1005
1007
  database: None,
@@ -1029,6 +1031,7 @@ fn test_cte_alias_resolution() {
1029
1031
  config: ManifestConfig::default(),
1030
1032
  description: None,
1031
1033
  path: None,
1034
+ original_file_path: None,
1032
1035
  columns: mart_cols,
1033
1036
  compiled_code: Some(
1034
1037
  concat!(
@@ -1104,6 +1107,7 @@ fn test_select_star_chain_with_join() {
1104
1107
  resource_type: "source".to_string(),
1105
1108
  description: None,
1106
1109
  path: None,
1110
+ original_file_path: None,
1107
1111
  columns: user_cols,
1108
1112
  database: None,
1109
1113
  schema: None,
@@ -1130,6 +1134,7 @@ fn test_select_star_chain_with_join() {
1130
1134
  resource_type: "source".to_string(),
1131
1135
  description: None,
1132
1136
  path: None,
1137
+ original_file_path: None,
1133
1138
  columns: region_cols,
1134
1139
  database: None,
1135
1140
  schema: None,
@@ -1159,6 +1164,7 @@ fn test_select_star_chain_with_join() {
1159
1164
  config: ManifestConfig::default(),
1160
1165
  description: None,
1161
1166
  path: None,
1167
+ original_file_path: None,
1162
1168
  columns: stg_user_cols,
1163
1169
  compiled_code: Some("select id, name, area from users".to_string()),
1164
1170
  database: Some("mydb".to_string()),
@@ -1188,6 +1194,7 @@ fn test_select_star_chain_with_join() {
1188
1194
  config: ManifestConfig::default(),
1189
1195
  description: None,
1190
1196
  path: None,
1197
+ original_file_path: None,
1191
1198
  columns: stg_region_cols,
1192
1199
  compiled_code: Some("select id, region_name from regions".to_string()),
1193
1200
  database: Some("mydb".to_string()),
@@ -1221,6 +1228,7 @@ fn test_select_star_chain_with_join() {
1221
1228
  config: ManifestConfig::default(),
1222
1229
  description: None,
1223
1230
  path: None,
1231
+ original_file_path: None,
1224
1232
  columns: mart_cols,
1225
1233
  compiled_code: Some(
1226
1234
  concat!(
@@ -1322,6 +1330,7 @@ fn test_select_star_chain_with_cte_alias_and_join() {
1322
1330
  resource_type: "source".to_string(),
1323
1331
  description: None,
1324
1332
  path: None,
1333
+ original_file_path: None,
1325
1334
  columns: user_cols,
1326
1335
  database: None,
1327
1336
  schema: None,
@@ -1348,6 +1357,7 @@ fn test_select_star_chain_with_cte_alias_and_join() {
1348
1357
  resource_type: "source".to_string(),
1349
1358
  description: None,
1350
1359
  path: None,
1360
+ original_file_path: None,
1351
1361
  columns: region_cols,
1352
1362
  database: None,
1353
1363
  schema: None,
@@ -1377,6 +1387,7 @@ fn test_select_star_chain_with_cte_alias_and_join() {
1377
1387
  config: ManifestConfig::default(),
1378
1388
  description: None,
1379
1389
  path: None,
1390
+ original_file_path: None,
1380
1391
  columns: stg_user_cols,
1381
1392
  compiled_code: Some("select id, name, area from users".to_string()),
1382
1393
  database: Some("mydb".to_string()),
@@ -1406,6 +1417,7 @@ fn test_select_star_chain_with_cte_alias_and_join() {
1406
1417
  config: ManifestConfig::default(),
1407
1418
  description: None,
1408
1419
  path: None,
1420
+ original_file_path: None,
1409
1421
  columns: stg_region_cols,
1410
1422
  compiled_code: Some("select id, region_name from regions".to_string()),
1411
1423
  database: Some("mydb".to_string()),
@@ -1439,6 +1451,7 @@ fn test_select_star_chain_with_cte_alias_and_join() {
1439
1451
  config: ManifestConfig::default(),
1440
1452
  description: None,
1441
1453
  path: None,
1454
+ original_file_path: None,
1442
1455
  columns: mart_cols,
1443
1456
  compiled_code: Some(
1444
1457
  concat!(
@@ -36,6 +36,7 @@ fn make_test_manifest() -> Manifest {
36
36
  config: ManifestConfig::default(),
37
37
  description: None,
38
38
  path: None,
39
+ original_file_path: None,
39
40
  columns: stg_orders_cols,
40
41
  compiled_code: Some(
41
42
  "select id as order_id, user_id as customer_id, order_date, status from raw.orders"
@@ -67,6 +68,7 @@ fn make_test_manifest() -> Manifest {
67
68
  config: ManifestConfig::default(),
68
69
  description: None,
69
70
  path: None,
71
+ original_file_path: None,
70
72
  columns: orders_cols,
71
73
  compiled_code: Some("select o.order_id, o.customer_id, p.amount as total_amount from stg_orders o left join stg_payments p on o.order_id = p.order_id".to_string()),
72
74
  database: None,
@@ -93,6 +95,7 @@ fn make_test_manifest() -> Manifest {
93
95
  config: ManifestConfig::default(),
94
96
  description: None,
95
97
  path: None,
98
+ original_file_path: None,
96
99
  columns: stg_payments_cols,
97
100
  compiled_code: Some(
98
101
  "select id as payment_id, order_id, amount, payment_method from raw.payments"
@@ -123,6 +126,7 @@ fn make_test_manifest() -> Manifest {
123
126
  resource_type: "source".to_string(),
124
127
  description: None,
125
128
  path: None,
129
+ original_file_path: None,
126
130
  columns: source_cols,
127
131
  database: None,
128
132
  schema: None,
@@ -160,6 +164,7 @@ fn make_cross_model_manifest() -> Manifest {
160
164
  resource_type: "source".to_string(),
161
165
  description: None,
162
166
  path: None,
167
+ original_file_path: None,
163
168
  columns: raw_orders_cols,
164
169
  database: None,
165
170
  schema: None,
@@ -186,6 +191,7 @@ fn make_cross_model_manifest() -> Manifest {
186
191
  resource_type: "source".to_string(),
187
192
  description: None,
188
193
  path: None,
194
+ original_file_path: None,
189
195
  columns: raw_payments_cols,
190
196
  database: None,
191
197
  schema: None,
@@ -215,6 +221,7 @@ fn make_cross_model_manifest() -> Manifest {
215
221
  config: ManifestConfig::default(),
216
222
  description: None,
217
223
  path: None,
224
+ original_file_path: None,
218
225
  columns: stg_orders_cols,
219
226
  compiled_code: Some(
220
227
  "select id as order_id, user_id as customer_id, order_date, status from orders"
@@ -247,6 +254,7 @@ fn make_cross_model_manifest() -> Manifest {
247
254
  config: ManifestConfig::default(),
248
255
  description: None,
249
256
  path: None,
257
+ original_file_path: None,
250
258
  columns: stg_payments_cols,
251
259
  compiled_code: Some(
252
260
  "select id as payment_id, order_id, amount, payment_method from payments"
@@ -282,6 +290,7 @@ fn make_cross_model_manifest() -> Manifest {
282
290
  config: ManifestConfig::default(),
283
291
  description: None,
284
292
  path: None,
293
+ original_file_path: None,
285
294
  columns: orders_cols,
286
295
  compiled_code: Some(
287
296
  concat!(
@@ -321,6 +330,7 @@ fn make_cross_model_manifest() -> Manifest {
321
330
  config: ManifestConfig::default(),
322
331
  description: None,
323
332
  path: None,
333
+ original_file_path: None,
324
334
  columns: customers_cols,
325
335
  compiled_code: Some(
326
336
  concat!(
@@ -355,6 +365,7 @@ fn make_duplicate_name_manifest() -> Manifest {
355
365
  config: ManifestConfig::default(),
356
366
  description: None,
357
367
  path: None,
368
+ original_file_path: None,
358
369
  columns: {
359
370
  let mut cols = HashMap::new();
360
371
  cols.insert(
@@ -384,6 +395,7 @@ fn make_duplicate_name_manifest() -> Manifest {
384
395
  config: ManifestConfig::default(),
385
396
  description: None,
386
397
  path: None,
398
+ original_file_path: None,
387
399
  columns: {
388
400
  let mut cols = HashMap::new();
389
401
  cols.insert(
@@ -413,6 +425,7 @@ fn make_duplicate_name_manifest() -> Manifest {
413
425
  config: ManifestConfig::default(),
414
426
  description: None,
415
427
  path: None,
428
+ original_file_path: None,
416
429
  columns: {
417
430
  let mut cols = HashMap::new();
418
431
  cols.insert(
@@ -508,6 +521,7 @@ fn make_diamond_manifest() -> Manifest {
508
521
  config: ManifestConfig::default(),
509
522
  description: None,
510
523
  path: None,
524
+ original_file_path: None,
511
525
  columns: raw_cols,
512
526
  compiled_code: Some("select x, y from source_table".to_string()),
513
527
  database: None,
@@ -537,6 +551,7 @@ fn make_diamond_manifest() -> Manifest {
537
551
  config: ManifestConfig::default(),
538
552
  description: None,
539
553
  path: None,
554
+ original_file_path: None,
540
555
  columns: shared_cols,
541
556
  compiled_code: Some("select x, y from raw_data".to_string()),
542
557
  database: None,
@@ -564,6 +579,7 @@ fn make_diamond_manifest() -> Manifest {
564
579
  config: ManifestConfig::default(),
565
580
  description: None,
566
581
  path: None,
582
+ original_file_path: None,
567
583
  columns: left_cols,
568
584
  compiled_code: Some("select x from shared".to_string()),
569
585
  database: None,
@@ -591,6 +607,7 @@ fn make_diamond_manifest() -> Manifest {
591
607
  config: ManifestConfig::default(),
592
608
  description: None,
593
609
  path: None,
610
+ original_file_path: None,
594
611
  columns: right_cols,
595
612
  compiled_code: Some("select y from shared".to_string()),
596
613
  database: None,
@@ -623,6 +640,7 @@ fn make_diamond_manifest() -> Manifest {
623
640
  config: ManifestConfig::default(),
624
641
  description: None,
625
642
  path: None,
643
+ original_file_path: None,
626
644
  columns: out_cols,
627
645
  compiled_code: Some(
628
646
  "select l.x as lx, r.y as ry from left_model l join right_model r on 1=1"
@@ -663,6 +681,7 @@ pub(super) fn make_transformation_manifest() -> Manifest {
663
681
  resource_type: "source".to_string(),
664
682
  description: None,
665
683
  path: None,
684
+ original_file_path: None,
666
685
  columns: raw_cols,
667
686
  database: None,
668
687
  schema: None,
@@ -692,6 +711,7 @@ pub(super) fn make_transformation_manifest() -> Manifest {
692
711
  config: ManifestConfig::default(),
693
712
  description: None,
694
713
  path: None,
714
+ original_file_path: None,
695
715
  columns: scalar_cols,
696
716
  compiled_code: Some(
697
717
  concat!(
@@ -730,6 +750,7 @@ pub(super) fn make_transformation_manifest() -> Manifest {
730
750
  config: ManifestConfig::default(),
731
751
  description: None,
732
752
  path: None,
753
+ original_file_path: None,
733
754
  columns: pt_upper_cols,
734
755
  compiled_code: Some(
735
756
  concat!(
@@ -767,6 +788,7 @@ pub(super) fn make_transformation_manifest() -> Manifest {
767
788
  config: ManifestConfig::default(),
768
789
  description: None,
769
790
  path: None,
791
+ original_file_path: None,
770
792
  columns: pt_coalesce_cols,
771
793
  compiled_code: Some(
772
794
  concat!(
@@ -819,6 +841,7 @@ pub(super) fn make_reconverging_manifest() -> Manifest {
819
841
  config: ManifestConfig::default(),
820
842
  description: None,
821
843
  path: None,
844
+ original_file_path: None,
822
845
  columns: {
823
846
  let mut cols = HashMap::new();
824
847
  cols.insert(
@@ -512,6 +512,28 @@ pub fn collapse_intermediate(
512
512
  build_subgraph_with_transitive(graph, &keep)
513
513
  }
514
514
 
515
+ /// Filter graph to nodes matching all of the given regex patterns (AND logic).
516
+ ///
517
+ /// Each pattern is tested against the node's label and description (OR within a single pattern).
518
+ /// A node is kept only if every pattern matches at least one of those fields.
519
+ /// An empty slice returns the graph unchanged.
520
+ pub fn filter_by_search(graph: &LineageGraph, patterns: &[regex::Regex]) -> LineageGraph {
521
+ if patterns.is_empty() {
522
+ return graph.clone();
523
+ }
524
+ let keep: HashSet<NodeIndex> = graph
525
+ .node_indices()
526
+ .filter(|&idx| {
527
+ let node = &graph[idx];
528
+ patterns.iter().all(|re| {
529
+ re.is_match(&node.label)
530
+ || node.description.as_deref().is_some_and(|d| re.is_match(d))
531
+ })
532
+ })
533
+ .collect();
534
+ build_subgraph(graph, &keep)
535
+ }
536
+
515
537
  /// BFS traversal collecting nodes up to max_depth levels away
516
538
  fn bfs_collect(
517
539
  graph: &LineageGraph,
@@ -2233,4 +2255,142 @@ mod tests {
2233
2255
  let filtered = filter_output_node_types(&g, &["source".into(), "model".into()], true);
2234
2256
  insta::assert_snapshot!(render_mermaid(&filtered));
2235
2257
  }
2258
+
2259
+ // -- filter_by_search tests -----------------------------------------------
2260
+
2261
+ fn make_node_with_desc(unique_id: &str, label: &str, description: Option<&str>) -> NodeData {
2262
+ NodeData {
2263
+ unique_id: unique_id.into(),
2264
+ label: label.into(),
2265
+ node_type: NodeType::Model,
2266
+ file_path: None,
2267
+ description: description.map(|s| s.to_string()),
2268
+ materialization: None,
2269
+ tags: vec![],
2270
+ columns: vec![],
2271
+ exposure: None,
2272
+ }
2273
+ }
2274
+
2275
+ fn make_search_graph() -> LineageGraph {
2276
+ let mut g = LineageGraph::new();
2277
+ g.add_node(make_node_with_desc(
2278
+ "model.stg_orders",
2279
+ "stg_orders",
2280
+ Some("Staging model for order data"),
2281
+ ));
2282
+ g.add_node(make_node_with_desc(
2283
+ "model.stg_customers",
2284
+ "stg_customers",
2285
+ Some("Staging model for customer data"),
2286
+ ));
2287
+ g.add_node(make_node_with_desc(
2288
+ "model.order_summary",
2289
+ "order_summary",
2290
+ None,
2291
+ ));
2292
+ g.add_node(make_node_with_desc("model.payments", "payments", None));
2293
+ g
2294
+ }
2295
+
2296
+ fn re(pattern: &str) -> regex::Regex {
2297
+ regex::RegexBuilder::new(pattern)
2298
+ .case_insensitive(true)
2299
+ .build()
2300
+ .unwrap()
2301
+ }
2302
+
2303
+ #[test]
2304
+ fn test_search_by_label() {
2305
+ let g = make_search_graph();
2306
+ let result = filter_by_search(&g, &[re("order")]);
2307
+ let labels: HashSet<String> = result
2308
+ .node_indices()
2309
+ .map(|i| result[i].label.clone())
2310
+ .collect();
2311
+ assert!(labels.contains("stg_orders"));
2312
+ assert!(labels.contains("order_summary"));
2313
+ assert!(!labels.contains("stg_customers"));
2314
+ assert!(!labels.contains("payments"));
2315
+ }
2316
+
2317
+ #[test]
2318
+ fn test_search_by_description() {
2319
+ let g = make_search_graph();
2320
+ // "customer" only appears in stg_customers description
2321
+ let result = filter_by_search(&g, &[re("customer")]);
2322
+ let labels: HashSet<String> = result
2323
+ .node_indices()
2324
+ .map(|i| result[i].label.clone())
2325
+ .collect();
2326
+ assert!(labels.contains("stg_customers"));
2327
+ assert_eq!(result.node_count(), 1);
2328
+ }
2329
+
2330
+ #[test]
2331
+ fn test_search_case_insensitive() {
2332
+ let g = make_search_graph();
2333
+ let result_lower = filter_by_search(&g, &[re("staging")]);
2334
+ let result_upper = filter_by_search(&g, &[re("STAGING")]);
2335
+ let result_mixed = filter_by_search(&g, &[re("Staging")]);
2336
+ assert_eq!(result_lower.node_count(), result_upper.node_count());
2337
+ assert_eq!(result_lower.node_count(), result_mixed.node_count());
2338
+ assert_eq!(result_lower.node_count(), 2); // stg_orders and stg_customers descriptions
2339
+ }
2340
+
2341
+ #[test]
2342
+ fn test_search_no_match() {
2343
+ let g = make_search_graph();
2344
+ let result = filter_by_search(&g, &[re("nonexistent_xyz")]);
2345
+ assert_eq!(result.node_count(), 0);
2346
+ }
2347
+
2348
+ #[test]
2349
+ fn test_search_empty_patterns_returns_all() {
2350
+ let g = make_search_graph();
2351
+ let result = filter_by_search(&g, &[]);
2352
+ assert_eq!(result.node_count(), g.node_count());
2353
+ }
2354
+
2355
+ #[test]
2356
+ fn test_search_multiple_patterns_and_logic() {
2357
+ let g = make_search_graph();
2358
+ // Both "stg" AND "order" must match — only stg_orders qualifies
2359
+ let result = filter_by_search(&g, &[re("stg"), re("order")]);
2360
+ let labels: HashSet<String> = result
2361
+ .node_indices()
2362
+ .map(|i| result[i].label.clone())
2363
+ .collect();
2364
+ assert_eq!(result.node_count(), 1);
2365
+ assert!(labels.contains("stg_orders"));
2366
+ }
2367
+
2368
+ #[test]
2369
+ fn test_search_regex_alternation() {
2370
+ let g = make_search_graph();
2371
+ // OR via regex alternation: matches stg_orders, stg_customers, payments
2372
+ let result = filter_by_search(&g, &[re("customer|payment")]);
2373
+ let labels: HashSet<String> = result
2374
+ .node_indices()
2375
+ .map(|i| result[i].label.clone())
2376
+ .collect();
2377
+ assert!(labels.contains("stg_customers"));
2378
+ assert!(labels.contains("payments"));
2379
+ assert!(!labels.contains("stg_orders"));
2380
+ assert!(!labels.contains("order_summary"));
2381
+ }
2382
+
2383
+ #[test]
2384
+ fn test_search_regex_pattern() {
2385
+ let g = make_search_graph();
2386
+ // Regex: labels starting with "stg_"
2387
+ let result = filter_by_search(&g, &[re("^stg_")]);
2388
+ let labels: HashSet<String> = result
2389
+ .node_indices()
2390
+ .map(|i| result[i].label.clone())
2391
+ .collect();
2392
+ assert!(labels.contains("stg_orders"));
2393
+ assert!(labels.contains("stg_customers"));
2394
+ assert_eq!(result.node_count(), 2);
2395
+ }
2236
2396
  }