Skip to main content

mdbook_plotly/preprocessor/handlers/code_handler/
plot_obj_parser.rs

1pub use super::until;
2use super::until::{Color, Map};
3use crate::translate;
4use anyhow::{Result, anyhow};
5use plotly::{
6    Configuration, Layout, Plot, Trace,
7    layout::{Legend, Margin},
8};
9use serde_json::Value;
10
11pub mod bar_parser;
12pub mod candlestick_parser;
13pub mod density_mapbox_parser;
14pub mod histogram_parser;
15pub mod image_parser;
16pub mod ohlc_parser;
17pub mod pie_parser;
18pub mod sankey_parser;
19pub mod scatter_geo_parser;
20pub mod scatter_mapbox_parser;
21pub mod scatter_parser;
22pub mod scatter_polar_parser;
23pub mod table_parser;
24
25pub fn parse(plot_obj: &mut Value) -> Result<Plot> {
26    let mut plot = Plot::new();
27
28    let map = if let Some(map_obj) = plot_obj.get_mut("map") {
29        serde_json::from_value::<Map>(map_obj.take())?
30    } else {
31        Map::new()
32    };
33
34    if let Some(config_obj) = plot_obj.get_mut("config")
35        && config_obj.is_object()
36    {
37        let config = parse_config_obj(config_obj, &map)?;
38        plot.set_configuration(config);
39    }
40
41    if let Some(layout_obj) = plot_obj.get_mut("layout")
42        && layout_obj.is_object()
43    {
44        let layout = parse_layout_obj(layout_obj, &map)?;
45        plot.set_layout(layout);
46    }
47
48    if let Some(data_list) = plot_obj.get_mut("data")
49        && data_list.is_array()
50    {
51        for data in data_list.as_array_mut().unwrap_or_else(|| unreachable!()) {
52            let trace = parse_data_obj(data, &map)?;
53            plot.add_trace(trace);
54        }
55    }
56
57    Ok(plot)
58}
59
60fn parse_config_obj(config_obj: &mut Value, map: &Map) -> Result<Configuration> {
61    let config = translate! {
62        Configuration::new(),
63        config_obj,
64        map,
65        (static_plot, bool),
66        (typeset_math, bool),
67        (editable, bool),
68        (autosizable, bool),
69        // NOTE:
70        // Although this method is still in place, it is no longer valid as far as the documentation is concerned.
71        // Subsequent versions may remove this method without warning.
72        (responsive, bool),
73        (fill_frame, bool),
74        (frame_margins, f64),
75        (scroll_zoom, bool),
76        (show_axis_drag_handles, bool),
77        (show_axis_range_entry_boxes, bool),
78        (show_tips, bool),
79        (show_link, bool),
80        (send_data, bool),
81        (double_click_delay, usize),
82        (queue_length, usize),
83        (display_logo, bool),
84        (watermark, bool),
85    }?;
86
87    Ok(config)
88}
89
90fn parse_layout_obj(layout_obj: &mut Value, map: &Map) -> Result<Layout> {
91    let layout = translate! {
92        Layout::new(),
93        layout_obj,
94        map,
95        (title, String),
96        (show_legend, bool),
97        (height, usize),
98        (width, usize),
99        (colorway, Vec<Color>),
100        (plot_background_color, Color),
101        (separators, String),
102    }?;
103
104    let layout = if let Some(legend_obj) = layout_obj.get_mut("legend")
105        && legend_obj.is_object()
106    {
107        let legend = translate! {
108            Legend::new(),
109            legend_obj,
110            map,
111            (background_color, Color),
112            (border_color, Color),
113            (border_width, usize),
114            (x, f64),
115            (y, f64),
116            (trace_group_gap, usize),
117            (title, String),
118        }?;
119        layout.legend(legend)
120    } else {
121        layout
122    };
123
124    let layout = if let Some(margin_obj) = layout_obj.get_mut("margin")
125        && margin_obj.is_object()
126    {
127        let margin = translate! {
128            Margin::new(),
129            margin_obj,
130            map,
131            (left, usize),
132            (right, usize),
133            (top, usize),
134            (bottom, usize),
135            (pad, usize),
136            (auto_expand, bool)
137        }?;
138        layout.margin(margin)
139    } else {
140        layout
141    };
142
143    Ok(layout)
144}
145
146pub fn parse_data_obj(data_obj: &mut Value, map: &Map) -> Result<Box<dyn Trace>> {
147    let data_type = data_obj
148        .get("type")
149        .and_then(|v| v.as_str())
150        .ok_or_else(|| anyhow!("`type` must be a string"))?;
151    match data_type {
152        "bar" => bar_parser::parse_bar_data(data_obj, map).map(|v| v as Box<dyn Trace>),
153        "candlestick" => {
154            candlestick_parser::parse_candlestick_data(data_obj, map).map(|v| v as Box<dyn Trace>)
155        }
156        "density_mapbox" => density_mapbox_parser::parse_density_mapbox_data(data_obj, map)
157            .map(|v| v as Box<dyn Trace>),
158        "histogram" => {
159            histogram_parser::parse_histogram_data(data_obj, map).map(|v| v as Box<dyn Trace>)
160        }
161        "ohlc" => ohlc_parser::parse_ohlc_data(data_obj, map).map(|v| v as Box<dyn Trace>),
162        "image" => image_parser::parse_image_data(data_obj, map).map(|v| v as Box<dyn Trace>),
163        "pie" => pie_parser::parse_pie_data(data_obj, map).map(|v| v as Box<dyn Trace>),
164        "sankey" => sankey_parser::parse_sankey_data(data_obj, map).map(|v| v as Box<dyn Trace>),
165        "scatter" => scatter_parser::parse_scatter_data(data_obj, map).map(|v| v as Box<dyn Trace>),
166        "scatter_geo" => {
167            scatter_geo_parser::parse_scatter_geo_data(data_obj, map).map(|v| v as Box<dyn Trace>)
168        }
169        "scatter_mapbox" => scatter_mapbox_parser::parse_scatter_mapbox_data(data_obj, map)
170            .map(|v| v as Box<dyn Trace>),
171        "scatter_polar" => scatter_polar_parser::parse_scatter_polar_data(data_obj, map)
172            .map(|v| v as Box<dyn Trace>),
173        "table" => table_parser::parse_table_data(data_obj, map).map(|v| v as Box<dyn Trace>),
174        unexpected => Err(anyhow!("{} isn't a type in data", unexpected)),
175    }
176}