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