dlin-cli 0.2.0a1__tar.gz → 0.2.0a2__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 (107) hide show
  1. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/Cargo.lock +2 -2
  2. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/Cargo.toml +2 -2
  3. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/PKG-INFO +1 -1
  4. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin/src/cli.rs +211 -83
  5. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin/src/main.rs +60 -38
  6. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin/tests/integration_test.rs +8 -8
  7. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/single_model.rs +21 -1
  8. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/tests/core.rs +95 -0
  9. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/tests/mod.rs +149 -0
  10. dlin_cli-0.2.0a2/crates/dlin-core/src/render/column_graph.rs +621 -0
  11. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/mermaid.rs +10 -26
  12. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/mod.rs +21 -0
  13. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid.snap +13 -0
  14. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_mermaid_indirect_edge_label.snap +13 -0
  15. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain.snap +6 -0
  16. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__impact_plain_multi_hop.snap +6 -0
  17. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_dotted_table_name.snap +13 -0
  18. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_id_collision_avoided.snap +13 -0
  19. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_label_escaping.snap +13 -0
  20. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__mermaid_single_model.snap +13 -0
  21. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_no_sources.snap +6 -0
  22. dlin_cli-0.2.0a2/crates/dlin-core/src/render/snapshots/dlin_core__render__column_graph__tests__plain_single_model.snap +7 -0
  23. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/LICENSE +0 -0
  24. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/README.md +0 -0
  25. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin/Cargo.toml +0 -0
  26. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin/README.md +0 -0
  27. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/Cargo.toml +0 -0
  28. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/README.md +0 -0
  29. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/error.rs +0 -0
  30. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/builder.rs +0 -0
  31. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/cache.rs +0 -0
  32. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/cross_model.rs +0 -0
  33. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/impact.rs +0 -0
  34. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/mod.rs +0 -0
  35. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/schema.rs +0 -0
  36. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/tests/cache.rs +0 -0
  37. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/tests/impact.rs +0 -0
  38. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/column_lineage/types.rs +0 -0
  39. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/filter.rs +0 -0
  40. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/impact.rs +0 -0
  41. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/mod.rs +0 -0
  42. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot.snap +0 -0
  43. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_bfs_pseudoendpoint.snap +0 -0
  44. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_endpoints_fan_out.snap +0 -0
  45. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_endpoints_leaf_model.snap +0 -0
  46. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_multiple_focus_models.snap +0 -0
  47. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_no_source_exposure.snap +0 -0
  48. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__collapse_snapshot_preserve_focus.snap +0 -0
  49. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_node_type_filter.snap +0 -0
  50. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_select_filter.snap +0 -0
  51. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/snapshots/dlin_core__graph__filter__tests__snapshot_transitive_select_with_node_type.snap +0 -0
  52. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/graph/types.rs +0 -0
  53. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/input.rs +0 -0
  54. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/lib.rs +0 -0
  55. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/cache.rs +0 -0
  56. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/columns.rs +0 -0
  57. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/discovery.rs +0 -0
  58. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/jinja.rs +0 -0
  59. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/manifest.rs +0 -0
  60. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/mod.rs +0 -0
  61. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/project.rs +0 -0
  62. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/sql.rs +0 -0
  63. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/parser/yaml_schema.rs +0 -0
  64. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/ascii.rs +0 -0
  65. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/dot.rs +0 -0
  66. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/html.rs +0 -0
  67. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/impact.rs +0 -0
  68. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/json.rs +0 -0
  69. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/layout.rs +0 -0
  70. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/list.rs +0 -0
  71. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/plain.rs +0 -0
  72. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__group_by_node_type.snap +0 -0
  73. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_all_edge_types.snap +0 -0
  74. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb.snap +0 -0
  75. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_direction_tb_grouped.snap +0 -0
  76. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_group_by_directory.snap +0 -0
  77. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_lineage.snap +0 -0
  78. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__dot__tests__snapshot_transitive_edges.snap +0 -0
  79. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__html__tests__snapshot_html_json.snap +0 -0
  80. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json.snap +0 -0
  81. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_json_with_sql.snap +0 -0
  82. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text.snap +0 -0
  83. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__impact__tests__snapshot_impact_text_with_sql.snap +0 -0
  84. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_json_with_sql.snap +0 -0
  85. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_lineage.snap +0 -0
  86. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__json__tests__snapshot_node_metadata.snap +0 -0
  87. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_json.snap +0 -0
  88. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__list__tests__snapshot_list_plain.snap +0 -0
  89. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__group_by_node_type.snap +0 -0
  90. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__mixed_direct_and_transitive_edges.snap +0 -0
  91. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_escapes_quotes.snap +0 -0
  92. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_lineage.snap +0 -0
  93. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_single_model.snap +0 -0
  94. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_collapse.snap +0 -0
  95. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__show_columns_with_grouping.snap +0 -0
  96. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb.snap +0 -0
  97. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_direction_tb_grouped.snap +0 -0
  98. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_group_by_directory.snap +0 -0
  99. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__snapshot_lineage.snap +0 -0
  100. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__mermaid__tests__transitive_edge_rendering.snap +0 -0
  101. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__plain__tests__snapshot_plain.snap +0 -0
  102. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_json.snap +0 -0
  103. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/snapshots/dlin_core__render__summary__tests__snapshot_summary_text.snap +0 -0
  104. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/summary.rs +0 -0
  105. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/src/render/svg.rs +0 -0
  106. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/crates/dlin-core/tests/column_lineage_test.rs +0 -0
  107. {dlin_cli-0.2.0a1 → dlin_cli-0.2.0a2}/pyproject.toml +0 -0
@@ -231,7 +231,7 @@ checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28"
231
231
 
232
232
  [[package]]
233
233
  name = "dlin"
234
- version = "0.2.0-alpha.1"
234
+ version = "0.2.0-alpha.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-alpha.1"
250
+ version = "0.2.0-alpha.2"
251
251
  dependencies = [
252
252
  "anyhow",
253
253
  "clap",
@@ -3,7 +3,7 @@ members = ["crates/*"]
3
3
  resolver = "3"
4
4
 
5
5
  [workspace.package]
6
- version = "0.2.0-alpha.1"
6
+ version = "0.2.0-alpha.2"
7
7
  edition = "2024"
8
8
  license = "MIT"
9
9
  repository = "https://github.com/eitsupi/dlin"
@@ -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-alpha.1", path = "crates/dlin-core" }
37
+ dlin-core = { version = "0.2.0-alpha.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.0a1
3
+ Version: 0.2.0a2
4
4
  Classifier: Development Status :: 4 - Beta
5
5
  Classifier: Environment :: Console
6
6
  Classifier: Intended Audience :: Developers
@@ -55,8 +55,8 @@ Examples:
55
55
  dlin summary -o json # Project overview as JSON
56
56
  dlin check-manifest || dbt compile # Recompile if stale or files deleted
57
57
  git diff --name-only main | dlin graph # Lineage of changed files
58
- dlin column graph orders # Upstream: where do columns come from? (requires dbt compile)
59
- dlin column impact stg_orders --column order_id # Downstream: what depends on this column? (requires dbt compile)",
58
+ dlin column upstream orders # Upstream: where do columns come from? (requires dbt compile)
59
+ dlin column downstream stg_orders --column order_id # Downstream: what depends on this column? (requires dbt compile)",
60
60
  version
61
61
  )]
62
62
  pub struct Cli {
@@ -150,8 +150,8 @@ File paths are resolved to model names using dbt project configuration.
150
150
  Column-level analysis:
151
151
  This command works at the model level (bidirectional, with -u/-d depth control).
152
152
  For column-level lineage tracing, see:
153
- dlin column graph upstream — traces where each output column's data came from
154
- dlin column impact downstream — finds which outputs depend on a given column
153
+ dlin column upstream — traces where each output column's data came from
154
+ dlin column downstream — finds which outputs depend on a given column
155
155
  Both require manifest.json (run `dbt compile` first).",
156
156
  after_long_help = "\
157
157
  Examples:
@@ -207,8 +207,8 @@ Examples:
207
207
  dlin graph -o mermaid --collapse --show-columns # rich detail on fewer nodes
208
208
 
209
209
  # === Column-level analysis (requires manifest) ===
210
- dlin column graph orders # upstream: where do columns come from?
211
- dlin column impact stg_orders --column order_id # downstream: what depends on this column?"
210
+ dlin column upstream orders # upstream: where do columns come from?
211
+ dlin column downstream stg_orders --column order_id # downstream: what depends on this column?"
212
212
  )]
213
213
  pub struct GraphArgs {
214
214
  /// Model names to focus on (shows full lineage if omitted)
@@ -738,16 +738,16 @@ Column-level lineage and impact analysis.
738
738
  Unlike `dlin graph` (model-level, bidirectional, with -u/-d depth control),
739
739
  column analysis is split by direction — each subcommand covers one direction:
740
740
 
741
- graph upstream — traces where each output column's data came from
742
- impact downstream — finds which models/columns are affected by a column change
741
+ upstream — traces where each output column's data came from
742
+ downstream — finds which models/columns are affected by a column change
743
743
 
744
744
  There are no -u/-d depth flags; the full chain is always traversed in both cases.
745
745
 
746
746
  Both subcommands require manifest.json with compiled SQL (run `dbt compile` first).",
747
747
  after_long_help = "\
748
748
  Examples:
749
- dlin column graph orders # upstream: where do columns come from?
750
- dlin column impact stg_orders --column order_id # downstream: what depends on this column?"
749
+ dlin column upstream orders # upstream: where do columns come from?
750
+ dlin column downstream stg_orders --column order_id # downstream: what depends on this column?"
751
751
  )]
752
752
  pub struct ColumnArgs {
753
753
  #[command(subcommand)]
@@ -766,7 +766,7 @@ their raw source columns across the full DAG. There are no -u/-d depth flags
766
766
  — the entire upstream chain is always traversed automatically.
767
767
 
768
768
  To find what downstream models or columns would be affected by changing a
769
- specific column, use `dlin column impact` instead.
769
+ specific column, use `dlin column downstream` instead.
770
770
 
771
771
  Requires manifest.json with compiled SQL (run `dbt compile` first).
772
772
 
@@ -774,46 +774,55 @@ Column resolution order:
774
774
  1. YAML column definitions (schema.yml / models.yml)
775
775
  2. SQL inference from compiled_code (fallback when YAML is absent)
776
776
 
777
- Output: JSON array per model with the following structure:
778
- model model name
779
- traced_columns number of columns successfully traced
780
- total_columns total number of columns attempted
781
- columns[]
782
- column output column name
783
- transformation how the column was derived:
784
- direct passed through unchanged (including renames)
785
- aggregation aggregate function (SUM, COUNT, etc.)
786
- expression arithmetic or other expression
787
- cast type cast (CAST(x AS INT))
788
- conditional CASE WHEN expression
789
- unknown could not classify
790
- sources[]
791
- table source model or raw table name
792
- column source column name
793
- model_path[] intermediate models traversed (omitted if empty)
794
- errors[] parse or resolution errors (non-empty exit code 1)
777
+ Output format (-o/--output):
778
+ json (default) JSON array per model with the following structure:
779
+ model model name
780
+ traced_columns number of columns successfully traced
781
+ total_columns total number of columns attempted
782
+ columns[]
783
+ column output column name
784
+ transformation how the column was derived:
785
+ direct passed through unchanged (including renames)
786
+ aggregation aggregate function (SUM, COUNT, etc.)
787
+ expression arithmetic or other expression
788
+ cast type cast (CAST(x AS INT))
789
+ conditional CASE WHEN expression
790
+ unknown could not classify
791
+ sources[]
792
+ table source model or raw table name
793
+ column source column name
794
+ model_path[] intermediate models traversed (omitted if empty)
795
+ errors[] parse or resolution errors (non-empty → exit code 1)
796
+ plain human-readable text, one model per block
797
+ mermaid Mermaid flowchart (LR) with subgraphs per model
795
798
 
796
799
  Exit codes:
797
800
  0 Success
798
801
  1 Error (model not found, no manifest, analysis errors, etc.)",
799
802
  after_long_help = "\
800
803
  Examples:
801
- # Column lineage for a single model
802
- dlin column graph orders
804
+ # Column lineage for a single model (JSON output)
805
+ dlin column upstream orders
806
+
807
+ # Human-readable plain output
808
+ dlin column upstream orders -o plain
809
+
810
+ # Mermaid flowchart
811
+ dlin column upstream orders -o mermaid
803
812
 
804
813
  # Specific columns only
805
- dlin column graph orders --column order_id --column status
814
+ dlin column upstream orders --column order_id --column status
806
815
 
807
816
  # Multiple models
808
- dlin column graph orders stg_orders
817
+ dlin column upstream orders stg_orders
809
818
 
810
819
  # With explicit manifest path
811
- dlin column graph orders --manifest-path target/manifest.json
820
+ dlin column upstream orders --manifest-path target/manifest.json
812
821
 
813
822
  # BigQuery project
814
- dlin column graph orders --dialect bigquery"
823
+ dlin column upstream orders --dialect bigquery"
815
824
  )]
816
- Graph(ColumnGraphArgs),
825
+ Upstream(ColumnGraphArgs),
817
826
 
818
827
  /// Analyze downstream column-level impact of changing a column
819
828
  #[command(
@@ -824,33 +833,42 @@ Direction: downstream only. Starting from a specific column, follows forward
824
833
  edges to find all dependent models and columns. There are no -u/-d depth flags
825
834
  — all downstream dependents are always included.
826
835
 
827
- This is the reverse direction of `dlin column graph` (which traces upstream
828
- sources). To trace where a column's data comes from, use `dlin column graph`.
836
+ This is the reverse direction of `dlin column upstream` (which traces upstream
837
+ sources). To trace where a column's data comes from, use `dlin column upstream`.
829
838
 
830
839
  Takes a single model and one or more --column flags (required).
831
840
 
832
841
  Requires compiled SQL in manifest.json — run `dbt compile` first.
833
842
 
834
- Output: JSON array per column with affected downstream columns and models.
843
+ Output format (-o/--output):
844
+ json (default) JSON array per column with affected downstream columns and models
845
+ plain human-readable text, one source column per block
846
+ mermaid Mermaid flowchart (LR) showing impacted columns across models
835
847
 
836
848
  Exit codes:
837
849
  0 Success
838
850
  1 Error (model not found, no manifest, analysis errors, etc.)",
839
851
  after_long_help = "\
840
852
  Examples:
841
- # Impact of changing a single column
842
- dlin column impact stg_orders --column order_id
853
+ # Impact of changing a single column (JSON output)
854
+ dlin column downstream stg_orders --column order_id
855
+
856
+ # Human-readable plain output
857
+ dlin column downstream stg_orders --column order_id -o plain
858
+
859
+ # Mermaid flowchart
860
+ dlin column downstream stg_orders --column order_id -o mermaid
843
861
 
844
862
  # Impact of multiple columns
845
- dlin column impact stg_orders --column order_id --column status
863
+ dlin column downstream stg_orders --column order_id --column status
846
864
 
847
865
  # With explicit manifest path
848
- dlin column impact stg_orders --column order_id --manifest-path target/manifest.json
866
+ dlin column downstream stg_orders --column order_id --manifest-path target/manifest.json
849
867
 
850
868
  # BigQuery project
851
- dlin column impact stg_orders --column order_id --dialect bigquery"
869
+ dlin column downstream stg_orders --column order_id --dialect bigquery"
852
870
  )]
853
- Impact(ColumnImpactArgs),
871
+ Downstream(ColumnImpactArgs),
854
872
  }
855
873
 
856
874
  #[derive(Debug, clap::Args)]
@@ -863,6 +881,10 @@ pub struct ColumnGraphArgs {
863
881
  #[arg(long)]
864
882
  pub column: Vec<String>,
865
883
 
884
+ /// Output format: json (default), plain, mermaid
885
+ #[arg(short = 'o', long, default_value = "json")]
886
+ pub output: ColumnOutputFormat,
887
+
866
888
  /// SQL dialect for parsing (default: generic).
867
889
  /// [possible values: bigquery, snowflake, postgres, redshift, databricks, spark, trino, duckdb, mysql, clickhouse, oracle, hive, sqlite, presto, athena, teradata, doris, starrocks, materialize, risingwave, singlestore, cockroachdb, tidb, tsql, druid, solr, tableau, dune, fabric, drill, dremio, exasol, datafusion]
868
890
  #[arg(long)]
@@ -895,13 +917,17 @@ pub struct ColumnGraphArgs {
895
917
 
896
918
  #[derive(Debug, clap::Args)]
897
919
  pub struct ColumnImpactArgs {
898
- /// Model name to analyze column impact for
920
+ /// Model name to analyze column downstream impact for
899
921
  pub model: String,
900
922
 
901
923
  /// Columns to analyze impact for (required)
902
924
  #[arg(long, required = true)]
903
925
  pub column: Vec<String>,
904
926
 
927
+ /// Output format: json (default), plain, mermaid
928
+ #[arg(short = 'o', long, default_value = "json")]
929
+ pub output: ColumnOutputFormat,
930
+
905
931
  /// SQL dialect for parsing (default: generic).
906
932
  /// [possible values: bigquery, snowflake, postgres, redshift, databricks, spark, trino, duckdb, mysql, clickhouse, oracle, hive, sqlite, presto, athena, teradata, doris, starrocks, materialize, risingwave, singlestore, cockroachdb, tidb, tsql, druid, solr, tableau, dune, fabric, drill, dremio, exasol, datafusion]
907
933
  #[arg(long)]
@@ -1199,6 +1225,16 @@ pub enum ImpactOutputFormat {
1199
1225
  Json,
1200
1226
  }
1201
1227
 
1228
+ #[derive(Debug, Clone, PartialEq, Eq, clap::ValueEnum)]
1229
+ pub enum ColumnOutputFormat {
1230
+ /// Machine-readable JSON array (default)
1231
+ Json,
1232
+ /// Human-readable plain-text table
1233
+ Plain,
1234
+ /// Mermaid flowchart diagram
1235
+ Mermaid,
1236
+ }
1237
+
1202
1238
  #[cfg(test)]
1203
1239
  mod tests {
1204
1240
  use super::*;
@@ -1695,40 +1731,41 @@ mod tests {
1695
1731
  assert_eq!(cli.error_format, ErrorFormat::Json);
1696
1732
  }
1697
1733
 
1698
- fn unwrap_column_graph(cli: Cli) -> ColumnGraphArgs {
1734
+ fn unwrap_column_upstream(cli: Cli) -> ColumnGraphArgs {
1699
1735
  match cli.command {
1700
1736
  Command::Column(col) => match col.command {
1701
- ColumnCommand::Graph(args) => args,
1702
- _ => panic!("Expected Column graph subcommand"),
1737
+ ColumnCommand::Upstream(args) => args,
1738
+ _ => panic!("Expected Column upstream subcommand"),
1703
1739
  },
1704
1740
  _ => panic!("Expected Column subcommand"),
1705
1741
  }
1706
1742
  }
1707
1743
 
1708
- fn unwrap_column_impact(cli: Cli) -> ColumnImpactArgs {
1744
+ fn unwrap_column_downstream(cli: Cli) -> ColumnImpactArgs {
1709
1745
  match cli.command {
1710
1746
  Command::Column(col) => match col.command {
1711
- ColumnCommand::Impact(args) => args,
1712
- _ => panic!("Expected Column impact subcommand"),
1747
+ ColumnCommand::Downstream(args) => args,
1748
+ _ => panic!("Expected Column downstream subcommand"),
1713
1749
  },
1714
1750
  _ => panic!("Expected Column subcommand"),
1715
1751
  }
1716
1752
  }
1717
1753
 
1718
1754
  #[test]
1719
- fn test_column_lineage_subcommand() {
1720
- let args = unwrap_column_graph(
1721
- Cli::try_parse_from(["dlin", "column", "graph", "orders"]).unwrap(),
1755
+ fn test_column_upstream_subcommand() {
1756
+ let args = unwrap_column_upstream(
1757
+ Cli::try_parse_from(["dlin", "column", "upstream", "orders"]).unwrap(),
1722
1758
  );
1723
1759
  assert_eq!(args.model, &["orders"]);
1724
1760
  assert!(args.column.is_empty());
1725
1761
  }
1726
1762
 
1727
1763
  #[test]
1728
- fn test_column_lineage_with_column_filter() {
1729
- let args = unwrap_column_graph(
1764
+ fn test_column_upstream_with_column_filter() {
1765
+ let args = unwrap_column_upstream(
1730
1766
  Cli::try_parse_from([
1731
- "dlin", "column", "graph", "orders", "--column", "order_id", "--column", "status",
1767
+ "dlin", "column", "upstream", "orders", "--column", "order_id", "--column",
1768
+ "status",
1732
1769
  ])
1733
1770
  .unwrap(),
1734
1771
  );
@@ -1737,22 +1774,22 @@ mod tests {
1737
1774
  }
1738
1775
 
1739
1776
  #[test]
1740
- fn test_column_lineage_no_model() {
1777
+ fn test_column_upstream_no_model() {
1741
1778
  // model is required at the clap level
1742
- let result = Cli::try_parse_from(["dlin", "column", "graph"]);
1779
+ let result = Cli::try_parse_from(["dlin", "column", "upstream"]);
1743
1780
  assert!(
1744
1781
  result.is_err(),
1745
- "column graph should require at least one model"
1782
+ "column upstream should require at least one model"
1746
1783
  );
1747
1784
  }
1748
1785
 
1749
1786
  #[test]
1750
- fn test_column_impact_subcommand() {
1751
- let args = unwrap_column_impact(
1787
+ fn test_column_downstream_subcommand() {
1788
+ let args = unwrap_column_downstream(
1752
1789
  Cli::try_parse_from([
1753
1790
  "dlin",
1754
1791
  "column",
1755
- "impact",
1792
+ "downstream",
1756
1793
  "stg_orders",
1757
1794
  "--column",
1758
1795
  "order_id",
@@ -1764,19 +1801,19 @@ mod tests {
1764
1801
  }
1765
1802
 
1766
1803
  #[test]
1767
- fn test_column_impact_requires_column() {
1768
- // --column is required for column impact
1769
- let result = Cli::try_parse_from(["dlin", "column", "impact", "stg_orders"]);
1770
- assert!(result.is_err(), "column impact should require --column");
1804
+ fn test_column_downstream_requires_column() {
1805
+ // --column is required for column downstream
1806
+ let result = Cli::try_parse_from(["dlin", "column", "downstream", "stg_orders"]);
1807
+ assert!(result.is_err(), "column downstream should require --column");
1771
1808
  }
1772
1809
 
1773
1810
  #[test]
1774
- fn test_column_impact_multiple_columns() {
1775
- let args = unwrap_column_impact(
1811
+ fn test_column_downstream_multiple_columns() {
1812
+ let args = unwrap_column_downstream(
1776
1813
  Cli::try_parse_from([
1777
1814
  "dlin",
1778
1815
  "column",
1779
- "impact",
1816
+ "downstream",
1780
1817
  "stg_orders",
1781
1818
  "--column",
1782
1819
  "order_id",
@@ -1789,29 +1826,36 @@ mod tests {
1789
1826
  }
1790
1827
 
1791
1828
  #[test]
1792
- fn test_column_lineage_with_dialect() {
1793
- let args = unwrap_column_graph(
1794
- Cli::try_parse_from(["dlin", "column", "graph", "orders", "--dialect", "bigquery"])
1795
- .unwrap(),
1829
+ fn test_column_upstream_with_dialect() {
1830
+ let args = unwrap_column_upstream(
1831
+ Cli::try_parse_from([
1832
+ "dlin",
1833
+ "column",
1834
+ "upstream",
1835
+ "orders",
1836
+ "--dialect",
1837
+ "bigquery",
1838
+ ])
1839
+ .unwrap(),
1796
1840
  );
1797
1841
  assert_eq!(args.dialect, Some(polyglot_sql::DialectType::BigQuery));
1798
1842
  }
1799
1843
 
1800
1844
  #[test]
1801
- fn test_column_lineage_default_dialect() {
1802
- let args = unwrap_column_graph(
1803
- Cli::try_parse_from(["dlin", "column", "graph", "orders"]).unwrap(),
1845
+ fn test_column_upstream_default_dialect() {
1846
+ let args = unwrap_column_upstream(
1847
+ Cli::try_parse_from(["dlin", "column", "upstream", "orders"]).unwrap(),
1804
1848
  );
1805
1849
  assert!(args.dialect.is_none());
1806
1850
  }
1807
1851
 
1808
1852
  #[test]
1809
- fn test_column_impact_with_dialect() {
1810
- let args = unwrap_column_impact(
1853
+ fn test_column_downstream_with_dialect() {
1854
+ let args = unwrap_column_downstream(
1811
1855
  Cli::try_parse_from([
1812
1856
  "dlin",
1813
1857
  "column",
1814
- "impact",
1858
+ "downstream",
1815
1859
  "stg_orders",
1816
1860
  "--column",
1817
1861
  "order_id",
@@ -1862,7 +1906,7 @@ mod tests {
1862
1906
  ];
1863
1907
  for dialect in dialects {
1864
1908
  let cli =
1865
- Cli::try_parse_from(["dlin", "column", "graph", "model", "--dialect", dialect]);
1909
+ Cli::try_parse_from(["dlin", "column", "upstream", "model", "--dialect", dialect]);
1866
1910
  assert!(
1867
1911
  cli.is_ok(),
1868
1912
  "dialect '{}' should parse successfully, got: {:?}",
@@ -1877,7 +1921,7 @@ mod tests {
1877
1921
  let result = Cli::try_parse_from([
1878
1922
  "dlin",
1879
1923
  "column",
1880
- "graph",
1924
+ "upstream",
1881
1925
  "model",
1882
1926
  "--dialect",
1883
1927
  "unknown_db",
@@ -1888,6 +1932,90 @@ mod tests {
1888
1932
  );
1889
1933
  }
1890
1934
 
1935
+ // -- ColumnOutputFormat tests --------------------------------------------
1936
+
1937
+ #[test]
1938
+ fn test_column_upstream_default_output_is_json() {
1939
+ let args = unwrap_column_upstream(
1940
+ Cli::try_parse_from(["dlin", "column", "upstream", "orders"]).unwrap(),
1941
+ );
1942
+ assert!(matches!(args.output, ColumnOutputFormat::Json));
1943
+ }
1944
+
1945
+ #[test]
1946
+ fn test_column_upstream_output_plain() {
1947
+ let args = unwrap_column_upstream(
1948
+ Cli::try_parse_from(["dlin", "column", "upstream", "orders", "-o", "plain"]).unwrap(),
1949
+ );
1950
+ assert!(matches!(args.output, ColumnOutputFormat::Plain));
1951
+ }
1952
+
1953
+ #[test]
1954
+ fn test_column_upstream_output_mermaid() {
1955
+ let args = unwrap_column_upstream(
1956
+ Cli::try_parse_from(["dlin", "column", "upstream", "orders", "-o", "mermaid"]).unwrap(),
1957
+ );
1958
+ assert!(matches!(args.output, ColumnOutputFormat::Mermaid));
1959
+ }
1960
+
1961
+ #[test]
1962
+ fn test_column_upstream_invalid_output_rejected() {
1963
+ let result = Cli::try_parse_from(["dlin", "column", "upstream", "orders", "-o", "ascii"]);
1964
+ assert!(result.is_err(), "ascii is not a valid column output format");
1965
+ }
1966
+
1967
+ #[test]
1968
+ fn test_column_downstream_default_output_is_json() {
1969
+ let args = unwrap_column_downstream(
1970
+ Cli::try_parse_from([
1971
+ "dlin",
1972
+ "column",
1973
+ "downstream",
1974
+ "stg_orders",
1975
+ "--column",
1976
+ "order_id",
1977
+ ])
1978
+ .unwrap(),
1979
+ );
1980
+ assert!(matches!(args.output, ColumnOutputFormat::Json));
1981
+ }
1982
+
1983
+ #[test]
1984
+ fn test_column_downstream_output_plain() {
1985
+ let args = unwrap_column_downstream(
1986
+ Cli::try_parse_from([
1987
+ "dlin",
1988
+ "column",
1989
+ "downstream",
1990
+ "stg_orders",
1991
+ "--column",
1992
+ "order_id",
1993
+ "-o",
1994
+ "plain",
1995
+ ])
1996
+ .unwrap(),
1997
+ );
1998
+ assert!(matches!(args.output, ColumnOutputFormat::Plain));
1999
+ }
2000
+
2001
+ #[test]
2002
+ fn test_column_downstream_output_mermaid() {
2003
+ let args = unwrap_column_downstream(
2004
+ Cli::try_parse_from([
2005
+ "dlin",
2006
+ "column",
2007
+ "downstream",
2008
+ "stg_orders",
2009
+ "--column",
2010
+ "order_id",
2011
+ "--output",
2012
+ "mermaid",
2013
+ ])
2014
+ .unwrap(),
2015
+ );
2016
+ assert!(matches!(args.output, ColumnOutputFormat::Mermaid));
2017
+ }
2018
+
1891
2019
  // -- Debug subcommand tests -----------------------------------------------
1892
2020
 
1893
2021
  fn unwrap_debug(cli: Cli) -> DebugArgs {