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
14pub 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
40pub trait PlotHelper {
43 fn get_layout(&self) -> &Layout;
44 fn get_traces(&self) -> &Vec<Box<dyn Trace + 'static>>;
45
46 #[cfg(any(
47 feature = "static_export_chromedriver",
48 feature = "static_export_geckodriver",
49 feature = "static_export_default"
50 ))]
51 fn get_image_format(
52 &self,
53 extension: &str,
54 ) -> Result<ImageFormat, std::boxed::Box<(dyn std::error::Error + 'static)>> {
55 match extension {
56 "png" => Ok(ImageFormat::PNG),
57 "jpg" => Ok(ImageFormat::JPEG),
58 "jpeg" => Ok(ImageFormat::JPEG),
59 "webp" => Ok(ImageFormat::WEBP),
60 "svg" => Ok(ImageFormat::SVG),
61 _ => Err(format!("Unsupported image format: {extension}").into()),
62 }
63 }
64}
65
66impl<T> Plot for T
68where
69 T: PlotHelper + Serialize + Clone,
70{
71 fn plot(&self) {
72 let mut plot = Plotly::new();
73 plot.set_layout(self.get_layout().to_owned());
74 plot.add_traces(self.get_traces().to_owned());
75
76 match env::var("EVCXR_IS_RUNTIME") {
77 Ok(_) => plot.notebook_display(),
78 _ => plot.show(),
79 }
80 }
81
82 fn write_html(&self, path: impl Into<String>) {
83 let mut plot = Plotly::new();
84 plot.set_layout(self.get_layout().to_owned());
85 plot.add_traces(self.get_traces().to_owned());
86 plot.write_html(path.into());
87 }
88
89 fn to_json(&self) -> Result<String, serde_json::Error> {
90 serde_json::to_string(self)
91 }
92
93 fn to_html(&self) -> String {
94 let mut plot = Plotly::new();
95 plot.set_layout(self.get_layout().to_owned());
96 plot.add_traces(self.get_traces().to_owned());
97 plot.to_html()
98 }
99
100 fn to_inline_html(&self, plot_div_id: Option<&str>) -> String {
101 let mut plot = Plotly::new();
102 plot.set_layout(self.get_layout().to_owned());
103 plot.add_traces(self.get_traces().to_owned());
104 plot.to_inline_html(plot_div_id)
105 }
106
107 #[cfg(any(
108 feature = "static_export_chromedriver",
109 feature = "static_export_geckodriver",
110 feature = "static_export_default"
111 ))]
112 fn write_image(
113 &self,
114 path: impl Into<String>,
115 width: usize,
116 height: usize,
117 scale: f64,
118 ) -> Result<(), std::boxed::Box<(dyn std::error::Error + 'static)>> {
119 let mut plot = Plotly::new();
120 plot.set_layout(self.get_layout().to_owned());
121 plot.add_traces(self.get_traces().to_owned());
122
123 if let Some((filename, extension)) = path.into().rsplit_once('.') {
124 let format = self.get_image_format(extension)?;
125 plot.write_image(filename, format, width, height, scale)?;
126 } else {
127 Err("No extension provided for image.")?;
128 }
129
130 Ok(())
131 }
132}