plotlars/common/
plot.rs

1use std::env;
2
3use plotly::{Layout, Plot as Plotly, Trace};
4
5#[cfg(any(
6    feature = "static_export_chromedriver",
7    feature = "static_export_geckodriver",
8    feature = "static_export_default"
9))]
10use plotly_static::ImageFormat;
11
12use serde::Serialize;
13
14/// A trait representing a generic plot that can be displayed or rendered.
15pub trait Plot {
16    fn plot(&self);
17
18    fn write_html(&self, path: impl Into<String>);
19
20    fn to_json(&self) -> Result<String, serde_json::Error>;
21
22    fn to_html(&self) -> String;
23
24    fn to_inline_html(&self, plot_div_id: Option<&str>) -> String;
25
26    #[cfg(any(
27        feature = "static_export_chromedriver",
28        feature = "static_export_geckodriver",
29        feature = "static_export_default"
30    ))]
31    fn write_image(
32        &self,
33        path: impl Into<String>,
34        width: usize,
35        height: usize,
36        scale: f64,
37    ) -> Result<(), std::boxed::Box<(dyn std::error::Error + 'static)>>;
38}
39
40// Private helper trait containing methods not exposed publicly.
41pub(crate) trait PlotHelper {
42    fn get_layout(&self) -> &Layout;
43    fn get_traces(&self) -> &Vec<Box<dyn Trace + 'static>>;
44
45    #[cfg(any(
46        feature = "static_export_chromedriver",
47        feature = "static_export_geckodriver",
48        feature = "static_export_default"
49    ))]
50    fn get_image_format(
51        &self,
52        extension: &str,
53    ) -> Result<ImageFormat, std::boxed::Box<(dyn std::error::Error + 'static)>> {
54        match extension {
55            "png" => Ok(ImageFormat::PNG),
56            "jpg" => Ok(ImageFormat::JPEG),
57            "jpeg" => Ok(ImageFormat::JPEG),
58            "webp" => Ok(ImageFormat::WEBP),
59            "svg" => Ok(ImageFormat::SVG),
60            _ => Err(format!("Unsupported image format: {extension}").into()),
61        }
62    }
63}
64
65// Implement the public trait `Plot` for any type that implements `PlotHelper`.
66impl<T> Plot for T
67where
68    T: PlotHelper + Serialize + Clone,
69{
70    fn plot(&self) {
71        let mut plot = Plotly::new();
72        plot.set_layout(self.get_layout().to_owned());
73        plot.add_traces(self.get_traces().to_owned());
74
75        match env::var("EVCXR_IS_RUNTIME") {
76            Ok(_) => plot.notebook_display(),
77            _ => plot.show(),
78        }
79    }
80
81    fn write_html(&self, path: impl Into<String>) {
82        let mut plot = Plotly::new();
83        plot.set_layout(self.get_layout().to_owned());
84        plot.add_traces(self.get_traces().to_owned());
85        plot.write_html(path.into());
86    }
87
88    fn to_json(&self) -> Result<String, serde_json::Error> {
89        serde_json::to_string(self)
90    }
91
92    fn to_html(&self) -> String {
93        let mut plot = Plotly::new();
94        plot.set_layout(self.get_layout().to_owned());
95        plot.add_traces(self.get_traces().to_owned());
96        plot.to_html()
97    }
98
99    fn to_inline_html(&self, plot_div_id: Option<&str>) -> String {
100        let mut plot = Plotly::new();
101        plot.set_layout(self.get_layout().to_owned());
102        plot.add_traces(self.get_traces().to_owned());
103        plot.to_inline_html(plot_div_id)
104    }
105
106    #[cfg(any(
107        feature = "static_export_chromedriver",
108        feature = "static_export_geckodriver",
109        feature = "static_export_default"
110    ))]
111    fn write_image(
112        &self,
113        path: impl Into<String>,
114        width: usize,
115        height: usize,
116        scale: f64,
117    ) -> Result<(), std::boxed::Box<(dyn std::error::Error + 'static)>> {
118        let mut plot = Plotly::new();
119        plot.set_layout(self.get_layout().to_owned());
120        plot.add_traces(self.get_traces().to_owned());
121
122        if let Some((filename, extension)) = path.into().rsplit_once('.') {
123            let format = self.get_image_format(extension)?;
124            plot.write_image(filename, format, width, height, scale)?;
125        } else {
126            Err("No extension provided for image.")?;
127        }
128
129        Ok(())
130    }
131}