Skip to main content

mdbook_plotly/preprocessor/handlers/code_handler/
plot_obj_parser.rs

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