|
1 | 1 | //! High-level ggsql API. |
2 | 2 | //! |
3 | | -//! Two-stage API: `reader.execute()` → `render()`. |
| 3 | +//! Validation and query inspection without SQL execution. |
4 | 4 |
|
5 | | -use crate::naming; |
6 | 5 | use crate::parser; |
7 | | -use crate::plot::Plot; |
8 | | -use crate::{DataFrame, Result}; |
9 | | -use std::collections::HashMap; |
10 | | - |
11 | | -#[cfg(feature = "vegalite")] |
12 | | -use crate::writer::Writer; |
| 6 | +use crate::Result; |
13 | 7 |
|
14 | 8 | // ============================================================================ |
15 | 9 | // Core Types |
16 | 10 | // ============================================================================ |
17 | 11 |
|
18 | | -/// Result of `reader.execute()`, ready for rendering. |
19 | | -pub struct Spec { |
20 | | - /// Single resolved plot specification |
21 | | - plot: Plot, |
22 | | - /// Internal data map (global + layer-specific DataFrames) |
23 | | - data: HashMap<String, DataFrame>, |
24 | | - /// Cached metadata about the prepared visualization |
25 | | - metadata: Metadata, |
26 | | - /// The main SQL query that was executed |
27 | | - sql: String, |
28 | | - /// The raw VISUALISE portion text |
29 | | - visual: String, |
30 | | - /// Per-layer filter/source queries (None = uses global data directly) |
31 | | - layer_sql: Vec<Option<String>>, |
32 | | - /// Per-layer stat transform queries (None = no stat transform) |
33 | | - stat_sql: Vec<Option<String>>, |
34 | | - /// Validation warnings from preparation |
35 | | - warnings: Vec<ValidationWarning>, |
36 | | -} |
37 | | - |
38 | | -impl Spec { |
39 | | - /// Create a new Spec from PreparedData |
40 | | - pub(crate) fn new( |
41 | | - plot: Plot, |
42 | | - data: HashMap<String, DataFrame>, |
43 | | - sql: String, |
44 | | - visual: String, |
45 | | - layer_sql: Vec<Option<String>>, |
46 | | - stat_sql: Vec<Option<String>>, |
47 | | - warnings: Vec<ValidationWarning>, |
48 | | - ) -> Self { |
49 | | - // Compute metadata from data |
50 | | - let (rows, columns) = if let Some(df) = data.get(naming::GLOBAL_DATA_KEY) { |
51 | | - let cols: Vec<String> = df |
52 | | - .get_column_names() |
53 | | - .iter() |
54 | | - .map(|s| s.to_string()) |
55 | | - .collect(); |
56 | | - (df.height(), cols) |
57 | | - } else if let Some(df) = data.values().next() { |
58 | | - let cols: Vec<String> = df |
59 | | - .get_column_names() |
60 | | - .iter() |
61 | | - .map(|s| s.to_string()) |
62 | | - .collect(); |
63 | | - (df.height(), cols) |
64 | | - } else { |
65 | | - (0, Vec::new()) |
66 | | - }; |
67 | | - |
68 | | - let layer_count = plot.layers.len(); |
69 | | - let metadata = Metadata { |
70 | | - rows, |
71 | | - columns, |
72 | | - layer_count, |
73 | | - }; |
74 | | - |
75 | | - Self { |
76 | | - plot, |
77 | | - data, |
78 | | - metadata, |
79 | | - sql, |
80 | | - visual, |
81 | | - layer_sql, |
82 | | - stat_sql, |
83 | | - warnings, |
84 | | - } |
85 | | - } |
86 | | - |
87 | | - /// Render to output format (e.g., Vega-Lite JSON). |
88 | | - #[cfg(feature = "vegalite")] |
89 | | - pub fn render(&self, writer: &dyn Writer) -> Result<String> { |
90 | | - writer.write(&self.plot, &self.data) |
91 | | - } |
92 | | - |
93 | | - /// Get the resolved plot specification. |
94 | | - pub fn plot(&self) -> &Plot { |
95 | | - &self.plot |
96 | | - } |
97 | | - |
98 | | - /// Get visualization metadata. |
99 | | - pub fn metadata(&self) -> &Metadata { |
100 | | - &self.metadata |
101 | | - } |
102 | | - |
103 | | - /// Number of layers. |
104 | | - pub fn layer_count(&self) -> usize { |
105 | | - self.plot.layers.len() |
106 | | - } |
107 | | - |
108 | | - /// Get global data (main query result). |
109 | | - pub fn data(&self) -> Option<&DataFrame> { |
110 | | - self.data.get(naming::GLOBAL_DATA_KEY) |
111 | | - } |
112 | | - |
113 | | - /// Get layer-specific data (from FILTER or FROM clause). |
114 | | - pub fn layer_data(&self, layer_index: usize) -> Option<&DataFrame> { |
115 | | - self.data.get(&naming::layer_key(layer_index)) |
116 | | - } |
117 | | - |
118 | | - /// Get stat transform data (e.g., histogram bins, density estimates). |
119 | | - pub fn stat_data(&self, layer_index: usize) -> Option<&DataFrame> { |
120 | | - self.layer_data(layer_index) |
121 | | - } |
122 | | - |
123 | | - /// Get internal data map (all DataFrames by key). |
124 | | - pub fn data_map(&self) -> &HashMap<String, DataFrame> { |
125 | | - &self.data |
126 | | - } |
127 | | - |
128 | | - /// The main SQL query that was executed. |
129 | | - pub fn sql(&self) -> &str { |
130 | | - &self.sql |
131 | | - } |
132 | | - |
133 | | - /// The VISUALISE portion (raw text). |
134 | | - pub fn visual(&self) -> &str { |
135 | | - &self.visual |
136 | | - } |
137 | | - |
138 | | - /// Layer filter/source query, or `None` if using global data. |
139 | | - pub fn layer_sql(&self, layer_index: usize) -> Option<&str> { |
140 | | - self.layer_sql.get(layer_index).and_then(|s| s.as_deref()) |
141 | | - } |
142 | | - |
143 | | - /// Stat transform query, or `None` if no stat transform. |
144 | | - pub fn stat_sql(&self, layer_index: usize) -> Option<&str> { |
145 | | - self.stat_sql.get(layer_index).and_then(|s| s.as_deref()) |
146 | | - } |
147 | | - |
148 | | - /// Validation warnings from preparation. |
149 | | - pub fn warnings(&self) -> &[ValidationWarning] { |
150 | | - &self.warnings |
151 | | - } |
152 | | -} |
153 | | - |
154 | | -/// Metadata about the prepared visualization. |
155 | | -#[derive(Debug, Clone)] |
156 | | -pub struct Metadata { |
157 | | - pub rows: usize, |
158 | | - pub columns: Vec<String>, |
159 | | - pub layer_count: usize, |
160 | | -} |
161 | | - |
162 | 12 | /// Result of `validate()` - query inspection and validation without SQL execution. |
163 | 13 | pub struct Validated { |
164 | 14 | sql: String, |
|
0 commit comments