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