1pub use super::until;
2use super::until::{Color, Map};
3use crate::{translate, translate_enum};
4use anyhow::{Result, anyhow};
5use plotly::{Configuration, Layout, Plot, Trace};
6use serde_json::Value;
7
8pub mod bar_parser;
9pub mod box_plot_parser;
10pub mod candlestick_parser;
11pub mod contour_parser;
12pub mod density_mapbox_parser;
13pub mod heat_map_parser;
14pub mod histogram_parser;
15pub mod image_parser;
16pub mod mesh3d_parser;
17pub mod ohlc_parser;
18pub mod pie_parser;
19pub mod sankey_parser;
20pub mod scatter3d_parser;
21pub mod scatter_geo_parser;
22pub mod scatter_mapbox_parser;
23pub mod scatter_parser;
24pub mod scatter_polar_parser;
25pub mod surface_parser;
26pub mod table_parser;
27
28pub fn parse(plot_obj: &mut Value) -> Result<Plot> {
29 let mut plot = Plot::new();
30
31 let map = if let Some(map_obj) = plot_obj.get_mut("map") {
32 serde_json::from_value::<Map>(map_obj.take())?
33 } else {
34 Map::new()
35 };
36
37 if let Some(config_obj) = plot_obj.get_mut("config")
38 && config_obj.is_object()
39 {
40 let config = parse_config_obj(config_obj, &map)?;
41 plot.set_configuration(config);
42 }
43
44 if let Some(layout_obj) = plot_obj.get_mut("layout")
45 && layout_obj.is_object()
46 {
47 let layout = parse_layout_obj(layout_obj, &map)?;
48 plot.set_layout(layout);
49 }
50
51 if let Some(data_list) = plot_obj.get_mut("data")
52 && data_list.is_array()
53 {
54 for data in data_list.as_array_mut().unwrap_or_else(|| unreachable!()) {
55 let trace = parse_data_obj(data, &map)?;
56 plot.add_trace(trace);
57 }
58 }
59
60 Ok(plot)
61}
62
63fn parse_config_obj(config_obj: &mut Value, map: &Map) -> Result<Configuration> {
64 use plotly::configuration::{DisplayModeBar, DoubleClick};
65
66 let config = translate! {
67 Configuration::new(),
68 config_obj,
69 map,
70 (static_plot, bool),
71 (typeset_math, bool),
72 (editable, bool),
73 (autosizable, bool),
74 (fill_frame, bool),
75 (frame_margins, f64),
76 (scroll_zoom, bool),
77 (show_axis_drag_handles, bool),
78 (show_axis_range_entry_boxes, bool),
79 (show_tips, bool),
80 (show_link, bool),
81 (send_data, bool),
82 (show_edit_in_chart_studio, bool),
83 (double_click_delay, usize),
84 (queue_length, usize),
85 (display_logo, bool),
86 (watermark, bool),
87 }?;
88
89 let config = translate_enum! {
90 config,
91 config_obj,
92 map,
93 (display_mode_bar, {
94 "hover" => DisplayModeBar::Hover,
95 "true" => DisplayModeBar::True,
96 "false" => DisplayModeBar::False,
97 }),
98 (double_click, {
99 "false" => DoubleClick::False,
100 "reset" => DoubleClick::Reset,
101 "autosize" => DoubleClick::AutoSize,
102 "reset+autosize"=> DoubleClick::ResetAutoSize,
103 }),
104 }?;
105
106 Ok(config)
107}
108
109fn parse_layout_obj(layout_obj: &mut Value, map: &Map) -> Result<Layout> {
110 use plotly::layout::{GroupClick, ItemClick, ItemSizing, Legend, Margin, TraceOrder, VAlign};
111
112 let layout = translate! {
113 Layout::new(),
114 layout_obj,
115 map,
116 (title, String),
117 (show_legend, bool),
118 (auto_size, bool),
119 (height, usize),
120 (width, usize),
121 (colorway, Vec<Color>),
122 (plot_background_color, Color),
123 (paper_background_color, Color),
124 (separators, String),
125 }?;
126
127 let layout = if let Some(legend_obj) = layout_obj.get_mut("legend")
128 && legend_obj.is_object()
129 {
130 let legend = translate! {
131 Legend::new(),
132 legend_obj,
133 map,
134 (background_color, Color),
135 (border_color, Color),
136 (border_width, usize),
137 (x, f64),
138 (y, f64),
139 (trace_group_gap, usize),
140 (item_width, usize),
141 (title, String),
142 }?;
143
144 let legend = translate_enum! {
145 legend,
146 legend_obj,
147 map,
148 (trace_order, {
149 "reversed" => TraceOrder::Reversed,
150 "grouped" => TraceOrder::Grouped,
151 "reversed+grouped"=> TraceOrder::ReversedGrouped,
152 "normal" => TraceOrder::Normal,
153 }),
154 (item_sizing, {
155 "trace" => ItemSizing::Trace,
156 "constant" => ItemSizing::Constant,
157 }),
158 (item_click, {
159 "toggle" => ItemClick::Toggle,
160 "toggleothers" => ItemClick::ToggleOthers,
161 "false" => ItemClick::False,
162 }),
163 (item_double_click, {
164 "toggle" => ItemClick::Toggle,
165 "toggleothers" => ItemClick::ToggleOthers,
166 "false" => ItemClick::False,
167 }),
168 (valign, {
169 "top" => VAlign::Top,
170 "middle" => VAlign::Middle,
171 "bottom" => VAlign::Bottom,
172 }),
173 (group_click, {
174 "toggleitem" => GroupClick::ToggleItem,
175 "togglegroup"=> GroupClick::ToggleGroup,
176 }),
177 }?;
178
179 layout.legend(legend)
180 } else {
181 layout
182 };
183
184 let layout = if let Some(margin_obj) = layout_obj.get_mut("margin")
185 && margin_obj.is_object()
186 {
187 let margin = translate! {
188 Margin::new(),
189 margin_obj,
190 map,
191 (left, usize),
192 (right, usize),
193 (top, usize),
194 (bottom, usize),
195 (pad, usize),
196 (auto_expand, bool)
197 }?;
198 layout.margin(margin)
199 } else {
200 layout
201 };
202
203 Ok(layout)
204}
205
206pub fn parse_data_obj(data_obj: &mut Value, map: &Map) -> Result<Box<dyn Trace>> {
207 let data_type = data_obj
208 .get("type")
209 .and_then(|v| v.as_str())
210 .ok_or_else(|| anyhow!("`type` must be a string"))?;
211 match data_type {
212 "bar" => bar_parser::parse_bar_data(data_obj, map).map(|v| v as Box<dyn Trace>),
213 "box" => box_plot_parser::parse_box_plot_data(data_obj, map).map(|v| v as Box<dyn Trace>),
214 "candlestick" => {
215 candlestick_parser::parse_candlestick_data(data_obj, map).map(|v| v as Box<dyn Trace>)
216 }
217 "contour" => contour_parser::parse_contour_data(data_obj, map).map(|v| v as Box<dyn Trace>),
218 "density_mapbox" => density_mapbox_parser::parse_density_mapbox_data(data_obj, map)
219 .map(|v| v as Box<dyn Trace>),
220 "heatmap" => {
221 heat_map_parser::parse_heat_map_data(data_obj, map).map(|v| v as Box<dyn Trace>)
222 }
223 "histogram" => {
224 histogram_parser::parse_histogram_data(data_obj, map).map(|v| v as Box<dyn Trace>)
225 }
226 "ohlc" => ohlc_parser::parse_ohlc_data(data_obj, map).map(|v| v as Box<dyn Trace>),
227 "image" => image_parser::parse_image_data(data_obj, map).map(|v| v as Box<dyn Trace>),
228 "mesh3d" => mesh3d_parser::parse_mesh3d_data(data_obj, map).map(|v| v as Box<dyn Trace>),
229 "pie" => pie_parser::parse_pie_data(data_obj, map).map(|v| v as Box<dyn Trace>),
230 "sankey" => sankey_parser::parse_sankey_data(data_obj, map).map(|v| v as Box<dyn Trace>),
231 "scatter" => scatter_parser::parse_scatter_data(data_obj, map).map(|v| v as Box<dyn Trace>),
232 "scatter3d" => {
233 scatter3d_parser::parse_scatter3d_data(data_obj, map).map(|v| v as Box<dyn Trace>)
234 }
235 "scatter_geo" => {
236 scatter_geo_parser::parse_scatter_geo_data(data_obj, map).map(|v| v as Box<dyn Trace>)
237 }
238 "scatter_mapbox" => scatter_mapbox_parser::parse_scatter_mapbox_data(data_obj, map)
239 .map(|v| v as Box<dyn Trace>),
240 "scatter_polar" => scatter_polar_parser::parse_scatter_polar_data(data_obj, map)
241 .map(|v| v as Box<dyn Trace>),
242 "surface" => surface_parser::parse_surface_data(data_obj, map).map(|v| v as Box<dyn Trace>),
243 "table" => table_parser::parse_table_data(data_obj, map).map(|v| v as Box<dyn Trace>),
244 unexpected => Err(anyhow!("{} isn't a type in data", unexpected)),
245 }
246}