1use serde::{ser::Serializer, Serialize};
2use serde_repr::Serialize_repr;
3
4#[derive(Serialize, Debug, Clone)]
5#[serde(rename_all = "lowercase")]
6pub enum ImageButtonFormats {
7 Png,
8 Svg,
9 Jpeg,
10 Webp,
11}
12
13#[serde_with::skip_serializing_none]
14#[derive(Serialize, Debug, Default, Clone)]
15pub struct ToImageButtonOptions {
16 format: Option<ImageButtonFormats>,
17 filename: Option<String>,
18 height: Option<usize>,
19 width: Option<usize>,
20 scale: Option<usize>,
21}
22
23impl ToImageButtonOptions {
24 pub fn new() -> Self {
26 Default::default()
27 }
28
29 pub fn format(mut self, format: ImageButtonFormats) -> Self {
31 self.format = Some(format);
32 self
33 }
34
35 pub fn filename(mut self, filename: &str) -> Self {
37 self.filename = Some(filename.to_string());
38 self
39 }
40
41 pub fn height(mut self, height: usize) -> Self {
43 self.height = Some(height);
44 self
45 }
46
47 pub fn width(mut self, width: usize) -> Self {
49 self.width = Some(width);
50 self
51 }
52
53 pub fn scale(mut self, scale: usize) -> Self {
56 self.scale = Some(scale);
57 self
58 }
59}
60
61#[derive(Debug, Clone)]
62pub enum DisplayModeBar {
63 Hover,
64 True,
65 False,
66}
67
68impl Serialize for DisplayModeBar {
69 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
70 where
71 S: Serializer,
72 {
73 match *self {
74 Self::Hover => serializer.serialize_str("hover"),
75 Self::True => serializer.serialize_bool(true),
76 Self::False => serializer.serialize_bool(false),
77 }
78 }
79}
80
81#[derive(Serialize, Debug, Clone)]
82#[serde(rename_all = "camelCase")]
83pub enum ModeBarButtonName {
84 Zoom2d,
85 Pan2d,
86 Select2d,
87 Lasso2d,
88 ZoomIn2d,
89 ZoomOut2d,
90 AutoScale2d,
91 ResetScale2d,
92 Zoom3d,
93 Pan3d,
94 OrbitRotation,
95 TableRotation,
96 ResetCameraDefault3d,
97 ResetCameraLastSave3d,
98 HoverClosest3d,
99 HoverClosestCartesian,
100 HoverCompareCartesian,
101 ZoomInGeo,
102 ZoomOutGeo,
103 ResetGeo,
104 HoverClosestGeo,
105 HoverClosestGl2d,
106 HoverClosestPie,
107 ToggleHover,
108 ResetViews,
109 ToImage,
110 SendDataToCloud,
111 ToggleSpikelines,
112 ResetViewMapbox,
113 ZoomInMapbox,
114 ZoomOutMapbox,
115}
116
117#[derive(Debug, Clone)]
118pub enum DoubleClick {
119 False,
120 Reset,
121 AutoSize,
122 ResetAutoSize,
123}
124
125impl Serialize for DoubleClick {
126 fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
127 where
128 S: Serializer,
129 {
130 match *self {
131 Self::False => serializer.serialize_bool(false),
132 Self::Reset => serializer.serialize_str("reset"),
133 Self::AutoSize => serializer.serialize_str("autosize"),
134 Self::ResetAutoSize => serializer.serialize_str("reset+autosize"),
135 }
136 }
137}
138
139#[derive(Serialize_repr, Debug, Clone)]
140#[repr(u8)]
141pub enum PlotGLPixelRatio {
142 One = 1,
143 Two,
144 Three,
145 Four,
146}
147
148#[serde_with::skip_serializing_none]
149#[derive(Serialize, Debug, Default, Clone)]
150#[serde(rename_all = "camelCase")]
151pub struct Configuration {
152 typeset_math: Option<bool>,
156 autosizable: Option<bool>,
157 scroll_zoom: Option<bool>,
158 fill_frame: Option<bool>,
159 frame_margins: Option<f64>,
160 editable: Option<bool>,
161 static_plot: Option<bool>,
162 to_image_button_options: Option<ToImageButtonOptions>,
163 display_mode_bar: Option<DisplayModeBar>,
164 mode_bar_buttons_to_remove: Option<Vec<ModeBarButtonName>>,
165 show_link: Option<bool>,
166 #[serde(rename = "plotlyServerURL")]
167 plotly_server_url: Option<String>,
168 #[serde(rename = "topojsonURL")]
169 topojson_url: Option<String>,
170 link_text: Option<String>,
171 mapbox_access_token: Option<String>,
172 show_edit_in_chart_studio: Option<bool>,
173 locale: Option<String>,
174 #[serde(rename = "displaylogo")]
175 display_logo: Option<bool>,
176 responsive: Option<bool>,
177 double_click: Option<DoubleClick>,
178 double_click_delay: Option<usize>,
179 show_axis_drag_handles: Option<bool>,
180 show_axis_range_entry_boxes: Option<bool>,
181 show_tips: Option<bool>,
182 send_data: Option<bool>,
183 watermark: Option<bool>,
184 #[serde(rename = "plotGlPixelRatio")]
185 plot_gl_pixel_ratio: Option<PlotGLPixelRatio>,
186 show_send_to_cloud: Option<bool>,
187 queue_length: Option<usize>,
188}
189
190impl Configuration {
191 pub fn new() -> Self {
194 Default::default()
195 }
196
197 pub fn static_plot(mut self, static_plot: bool) -> Self {
200 self.static_plot = Some(static_plot);
201 self
202 }
203
204 pub fn typeset_math(mut self, typeset_math: bool) -> Self {
207 self.typeset_math = Some(typeset_math);
208 self
209 }
210
211 pub fn plotly_server_url(mut self, plotly_server_url: &str) -> Self {
217 self.plotly_server_url = Some(plotly_server_url.to_string());
218 self
219 }
220
221 pub fn editable(mut self, editable: bool) -> Self {
225 self.editable = Some(editable);
226 self
227 }
228
229 pub fn autosizable(mut self, autosizable: bool) -> Self {
232 self.autosizable = Some(autosizable);
233 self
234 }
235
236 pub fn responsive(mut self, responsive: bool) -> Self {
239 self.responsive = Some(responsive);
240 self
241 }
242
243 pub fn fill_frame(mut self, fill_frame: bool) -> Self {
246 self.fill_frame = Some(fill_frame);
247 self
248 }
249
250 pub fn frame_margins(mut self, frame_margins: f64) -> Self {
253 self.frame_margins = Some(frame_margins);
255 self
256 }
257
258 pub fn scroll_zoom(mut self, scroll_zoom: bool) -> Self {
264 self.scroll_zoom = Some(scroll_zoom);
265 self
266 }
267
268 pub fn double_click(mut self, double_click: DoubleClick) -> Self {
276 self.double_click = Some(double_click);
277 self
278 }
279
280 pub fn double_click_delay(mut self, double_click_delay: usize) -> Self {
285 self.double_click_delay = Some(double_click_delay);
286 self
287 }
288
289 pub fn show_axis_drag_handles(mut self, show_axis_drag_handles: bool) -> Self {
291 self.show_axis_drag_handles = Some(show_axis_drag_handles);
292 self
293 }
294
295 pub fn show_axis_range_entry_boxes(mut self, show_axis_range_entry_boxes: bool) -> Self {
299 self.show_axis_range_entry_boxes = Some(show_axis_range_entry_boxes);
300 self
301 }
302
303 pub fn show_tips(mut self, show_tips: bool) -> Self {
306 self.show_tips = Some(show_tips);
307 self
308 }
309
310 pub fn show_link(mut self, show_link: bool) -> Self {
314 self.show_link = Some(show_link);
315 self
316 }
317
318 pub fn link_text(mut self, link_text: &str) -> Self {
320 self.link_text = Some(link_text.to_string());
321 self
322 }
323
324 pub fn send_data(mut self, send_data: bool) -> Self {
327 self.send_data = Some(send_data);
328 self
329 }
330
331 pub fn display_mode_bar(mut self, display_mode_bar: DisplayModeBar) -> Self {
336 self.display_mode_bar = Some(display_mode_bar);
337 self
338 }
339
340 pub fn show_send_to_cloud(mut self, show_send_to_cloud: bool) -> Self {
350 self.show_send_to_cloud = Some(show_send_to_cloud);
351 self
352 }
353
354 pub fn show_edit_in_chart_studio(mut self, show_edit_in_chart_studio: bool) -> Self {
359 self.show_edit_in_chart_studio = Some(show_edit_in_chart_studio);
360 self
361 }
362
363 pub fn mode_bar_buttons_to_remove(
365 mut self,
366 mode_bar_buttons_to_remove: Vec<ModeBarButtonName>,
367 ) -> Self {
368 self.mode_bar_buttons_to_remove = Some(mode_bar_buttons_to_remove);
369 self
370 }
371
372 pub fn to_image_button_options(
374 mut self,
375 to_image_button_options: ToImageButtonOptions,
376 ) -> Self {
377 self.to_image_button_options = Some(to_image_button_options);
378 self
379 }
380
381 pub fn display_logo(mut self, display_logo: bool) -> Self {
384 self.display_logo = Some(display_logo);
385 self
386 }
387
388 pub fn watermark(mut self, watermark: bool) -> Self {
390 self.watermark = Some(watermark);
391 self
392 }
393
394 pub fn plot_gl_pixel_ratio(mut self, plot_gl_pixel_ratio: PlotGLPixelRatio) -> Self {
396 self.plot_gl_pixel_ratio = Some(plot_gl_pixel_ratio);
397 self
398 }
399
400 pub fn topojson_url(mut self, topojson_url: &str) -> Self {
405 self.topojson_url = Some(topojson_url.to_string());
406 self
407 }
408
409 pub fn mapbox_access_token(mut self, mapbox_access_token: &str) -> Self {
413 self.mapbox_access_token = Some(mapbox_access_token.to_string());
414 self
415 }
416
417 pub fn queue_length(mut self, queue_length: usize) -> Self {
419 self.queue_length = Some(queue_length);
420 self
421 }
422
423 pub fn locale(mut self, locale: &str) -> Self {
427 self.locale = Some(locale.to_string());
428 self
429 }
430}
431
432#[cfg(test)]
433mod tests {
434 use serde_json::{json, to_value};
435
436 use super::*;
437
438 #[test]
439 fn serialize_image_button_formats() {
440 assert_eq!(to_value(ImageButtonFormats::Png).unwrap(), json!("png"));
441 assert_eq!(to_value(ImageButtonFormats::Svg).unwrap(), json!("svg"));
442 assert_eq!(to_value(ImageButtonFormats::Jpeg).unwrap(), json!("jpeg"));
443 assert_eq!(to_value(ImageButtonFormats::Webp).unwrap(), json!("webp"));
444 }
445 #[test]
446 fn serialize_to_image_button_options() {
447 let options = ToImageButtonOptions::new()
448 .format(ImageButtonFormats::Jpeg)
449 .filename("filename")
450 .height(500)
451 .width(250)
452 .scale(2);
453 let expected = json!({
454 "format": "jpeg",
455 "filename": "filename",
456 "height": 500,
457 "width": 250,
458 "scale": 2
459 });
460
461 assert_eq!(to_value(options).unwrap(), expected)
462 }
463
464 #[test]
465 fn serialize_display_mode_bar() {
466 assert_eq!(to_value(DisplayModeBar::Hover).unwrap(), json!("hover"));
467 assert_eq!(to_value(DisplayModeBar::True).unwrap(), json!(true));
468 assert_eq!(to_value(DisplayModeBar::False).unwrap(), json!(false));
469 }
470
471 #[test]
472 #[rustfmt::skip]
473 fn serialize_mode_bar_button_name() {
474 assert_eq!(to_value(ModeBarButtonName::Zoom2d).unwrap(), json!("zoom2d"));
475 assert_eq!(to_value(ModeBarButtonName::Pan2d).unwrap(), json!("pan2d"));
476 assert_eq!(to_value(ModeBarButtonName::Select2d).unwrap(), json!("select2d"));
477 assert_eq!(to_value(ModeBarButtonName::Lasso2d).unwrap(), json!("lasso2d"));
478 assert_eq!(to_value(ModeBarButtonName::ZoomIn2d).unwrap(), json!("zoomIn2d"));
479 assert_eq!(to_value(ModeBarButtonName::ZoomOut2d).unwrap(), json!("zoomOut2d"));
480 assert_eq!(to_value(ModeBarButtonName::AutoScale2d).unwrap(), json!("autoScale2d"));
481 assert_eq!(to_value(ModeBarButtonName::ResetScale2d).unwrap(), json!("resetScale2d"));
482 assert_eq!(to_value(ModeBarButtonName::Zoom3d).unwrap(), json!("zoom3d"));
483 assert_eq!(to_value(ModeBarButtonName::Pan3d).unwrap(), json!("pan3d"));
484 assert_eq!(to_value(ModeBarButtonName::ResetCameraDefault3d).unwrap(), json!("resetCameraDefault3d"));
485 assert_eq!(to_value(ModeBarButtonName::ResetCameraLastSave3d).unwrap(), json!("resetCameraLastSave3d"));
486 assert_eq!(to_value(ModeBarButtonName::HoverClosest3d).unwrap(), json!("hoverClosest3d"));
487 assert_eq!(to_value(ModeBarButtonName::OrbitRotation).unwrap(), json!("orbitRotation"));
488 assert_eq!(to_value(ModeBarButtonName::TableRotation).unwrap(), json!("tableRotation"));
489 assert_eq!(to_value(ModeBarButtonName::HoverClosestCartesian).unwrap(), json!("hoverClosestCartesian"));
490 assert_eq!(to_value(ModeBarButtonName::HoverCompareCartesian).unwrap(), json!("hoverCompareCartesian"));
491 assert_eq!(to_value(ModeBarButtonName::ZoomInGeo).unwrap(), json!("zoomInGeo"));
492 assert_eq!(to_value(ModeBarButtonName::ZoomOutGeo).unwrap(), json!("zoomOutGeo"));
493 assert_eq!(to_value(ModeBarButtonName::ResetGeo).unwrap(), json!("resetGeo"));
494 assert_eq!(to_value(ModeBarButtonName::HoverClosestGeo).unwrap(), json!("hoverClosestGeo"));
495 assert_eq!(to_value(ModeBarButtonName::HoverClosestGl2d).unwrap(), json!("hoverClosestGl2d"));
496 assert_eq!(to_value(ModeBarButtonName::HoverClosestPie).unwrap(), json!("hoverClosestPie"));
497 assert_eq!(to_value(ModeBarButtonName::ToggleHover).unwrap(), json!("toggleHover"));
498 assert_eq!(to_value(ModeBarButtonName::ResetViews).unwrap(), json!("resetViews"));
499 assert_eq!(to_value(ModeBarButtonName::ToImage).unwrap(), json!("toImage"));
500 assert_eq!(to_value(ModeBarButtonName::SendDataToCloud).unwrap(), json!("sendDataToCloud"));
501 assert_eq!(to_value(ModeBarButtonName::ToggleSpikelines).unwrap(), json!("toggleSpikelines"));
502 assert_eq!(to_value(ModeBarButtonName::ResetViewMapbox).unwrap(), json!("resetViewMapbox"));
503 assert_eq!(to_value(ModeBarButtonName::ZoomInMapbox).unwrap(), json!("zoomInMapbox"));
504 assert_eq!(to_value(ModeBarButtonName::ZoomOutMapbox).unwrap(), json!("zoomOutMapbox"));
505 }
506
507 #[test]
508 #[rustfmt::skip]
509 fn serialize_double_click() {
510 assert_eq!(to_value(DoubleClick::False).unwrap(), json!(false));
511 assert_eq!(to_value(DoubleClick::Reset).unwrap(), json!("reset"));
512 assert_eq!(to_value(DoubleClick::AutoSize).unwrap(), json!("autosize"));
513 assert_eq!(to_value(DoubleClick::ResetAutoSize).unwrap(), json!("reset+autosize"));
514 }
515
516 #[test]
517 fn serialize_plot_gl_pixel_ratio() {
518 assert_eq!(to_value(PlotGLPixelRatio::One).unwrap(), json!(1));
519 assert_eq!(to_value(PlotGLPixelRatio::Two).unwrap(), json!(2));
520 assert_eq!(to_value(PlotGLPixelRatio::Three).unwrap(), json!(3));
521 assert_eq!(to_value(PlotGLPixelRatio::Four).unwrap(), json!(4));
522 }
523
524 #[test]
525 fn serialize_configuration() {
526 let config = Configuration::new()
527 .static_plot(true)
528 .typeset_math(true)
529 .plotly_server_url("server_url")
530 .editable(false)
531 .autosizable(false)
532 .responsive(true)
533 .fill_frame(false)
534 .frame_margins(2.0)
535 .scroll_zoom(false)
536 .double_click(DoubleClick::ResetAutoSize)
537 .double_click_delay(50)
538 .show_axis_drag_handles(false)
539 .show_axis_range_entry_boxes(true)
540 .show_tips(false)
541 .show_link(true)
542 .link_text("link text")
543 .send_data(false)
544 .display_mode_bar(DisplayModeBar::Hover)
545 .show_send_to_cloud(true)
546 .show_edit_in_chart_studio(false)
547 .mode_bar_buttons_to_remove(vec![ModeBarButtonName::Zoom2d])
548 .to_image_button_options(ToImageButtonOptions::new())
549 .display_logo(false)
550 .watermark(true)
551 .plot_gl_pixel_ratio(PlotGLPixelRatio::Four)
552 .topojson_url("topojson_url")
553 .mapbox_access_token("123")
554 .queue_length(100)
555 .locale("en");
556
557 let expected = json!({
558 "staticPlot": true,
559 "typesetMath": true,
560 "plotlyServerURL": "server_url",
561 "editable": false,
562 "autosizable": false,
563 "responsive": true,
564 "fillFrame": false,
565 "frameMargins": 2.0,
566 "scrollZoom": false,
567 "doubleClick": "reset+autosize",
568 "doubleClickDelay": 50,
569 "showAxisDragHandles": false,
570 "showAxisRangeEntryBoxes": true,
571 "showTips": false,
572 "showLink": true,
573 "linkText": "link text",
574 "sendData": false,
575 "displayModeBar": "hover",
576 "showSendToCloud": true,
577 "showEditInChartStudio": false,
578 "modeBarButtonsToRemove": ["zoom2d"],
579 "toImageButtonOptions": {},
580 "displaylogo": false,
581 "watermark": true,
582 "plotGlPixelRatio": 4,
583 "topojsonURL": "topojson_url",
584 "mapboxAccessToken": "123",
585 "queueLength": 100,
586 "locale": "en"
587 });
588
589 assert_eq!(to_value(config).unwrap(), expected);
590 }
591}