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