pub mod themes;
pub mod update_menu;
use std::borrow::Cow;
use plotly_derive::FieldSetter;
use serde::{Serialize, Serializer};
use update_menu::UpdateMenu;
use crate::{
color::Color,
common::{
Anchor, AxisSide, Calendar, ColorBar, ColorScale, DashType, ExponentFormat, Font, Label,
Orientation, TickFormatStop, TickMode, Title,
},
private::{NumOrString, NumOrStringCollection},
};
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum AxisType {
#[serde(rename = "-")]
Default,
Linear,
Log,
Date,
Category,
MultiCategory,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum AxisConstrain {
Range,
Domain,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ConstrainDirection {
Left,
Center,
Right,
Top,
Middle,
Bottom,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum RangeMode {
Normal,
ToZero,
NonNegative,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum TicksDirection {
Outside,
Inside,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum TicksPosition {
Labels,
Boundaries,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ArrayShow {
All,
First,
Last,
None,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum BarMode {
Stack,
Group,
Overlay,
Relative,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum BarNorm {
#[serde(rename = "")]
Empty,
Fraction,
Percent,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum BoxMode {
Group,
Overlay,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ViolinMode {
Group,
Overlay,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum WaterfallMode {
Group,
Overlay,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum TraceOrder {
Reversed,
Grouped,
#[serde(rename = "reversed+grouped")]
ReversedGrouped,
Normal,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ItemSizing {
Trace,
Constant,
}
#[derive(Debug, Clone)]
pub enum ItemClick {
Toggle,
ToggleOthers,
False,
}
impl Serialize for ItemClick {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match *self {
Self::Toggle => serializer.serialize_str("toggle"),
Self::ToggleOthers => serializer.serialize_str("toggleothers"),
Self::False => serializer.serialize_bool(false),
}
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum GroupClick {
ToggleItem,
ToggleGroup,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Legend {
#[serde(rename = "bgcolor")]
background_color: Option<Box<dyn Color>>,
#[serde(rename = "bordercolor")]
border_color: Option<Box<dyn Color>>,
#[serde(rename = "borderwidth")]
border_width: Option<usize>,
font: Option<Font>,
orientation: Option<Orientation>,
#[serde(rename = "traceorder")]
trace_order: Option<TraceOrder>,
#[serde(rename = "tracegroupgap")]
trace_group_gap: Option<usize>,
#[serde(rename = "itemsizing")]
item_sizing: Option<ItemSizing>,
#[serde(rename = "itemclick")]
item_click: Option<ItemClick>,
#[serde(rename = "itemdoubleclick")]
item_double_click: Option<ItemClick>,
x: Option<f64>,
#[serde(rename = "xanchor")]
x_anchor: Option<Anchor>,
y: Option<f64>,
#[serde(rename = "yanchor")]
y_anchor: Option<Anchor>,
valign: Option<VAlign>,
title: Option<Title>,
#[serde(rename = "groupclick")]
group_click: Option<GroupClick>,
#[serde(rename = "itemwidth")]
item_width: Option<usize>,
}
impl Legend {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum VAlign {
Top,
Middle,
Bottom,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum HAlign {
Left,
Center,
Right,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Margin {
#[serde(rename = "l")]
left: Option<usize>,
#[serde(rename = "r")]
right: Option<usize>,
#[serde(rename = "t")]
top: Option<usize>,
#[serde(rename = "b")]
bottom: Option<usize>,
pad: Option<usize>,
#[serde(rename = "autoexpand")]
auto_expand: Option<bool>,
}
impl Margin {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct LayoutColorScale {
sequential: Option<ColorScale>,
#[serde(rename = "sequentialminus")]
sequential_minus: Option<ColorScale>,
diverging: Option<ColorScale>,
}
impl LayoutColorScale {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum SliderRangeMode {
Auto,
Fixed,
Match,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct RangeSliderYAxis {
#[serde(rename = "rangemode")]
range_mode: Option<SliderRangeMode>,
range: Option<NumOrStringCollection>,
}
impl RangeSliderYAxis {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct RangeSlider {
#[serde(rename = "bgcolor")]
background_color: Option<Box<dyn Color>>,
#[serde(rename = "bordercolor")]
border_color: Option<Box<dyn Color>>,
#[serde(rename = "borderwidth")]
border_width: Option<u64>,
#[serde(rename = "autorange")]
auto_range: Option<bool>,
range: Option<NumOrStringCollection>,
thickness: Option<f64>,
visible: Option<bool>,
#[serde(rename = "yaxis")]
y_axis: Option<RangeSliderYAxis>,
}
impl RangeSlider {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum SelectorStep {
Month,
Year,
Day,
Hour,
Minute,
Second,
All,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum StepMode {
Backward,
ToDate,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct SelectorButton {
visible: Option<bool>,
step: Option<SelectorStep>,
#[serde(rename = "stepmode")]
step_mode: Option<StepMode>,
count: Option<usize>,
label: Option<String>,
name: Option<String>,
#[serde(rename = "templateitemname")]
template_item_name: Option<String>,
}
impl SelectorButton {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct RangeSelector {
visible: Option<bool>,
buttons: Option<Vec<SelectorButton>>,
x: Option<f64>,
#[serde(rename = "xanchor")]
x_anchor: Option<Anchor>,
y: Option<f64>,
#[serde(rename = "yanchor")]
y_anchor: Option<Anchor>,
font: Option<Font>,
#[serde(rename = "bgcolor")]
background_color: Option<Box<dyn Color>>,
#[serde(rename = "activecolor")]
active_color: Option<Box<dyn Color>>,
#[serde(rename = "bordercolor")]
border_color: Option<Box<dyn Color>>,
#[serde(rename = "borderwidth")]
border_width: Option<usize>,
}
impl RangeSelector {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct ColorAxis {
cauto: Option<bool>,
cmin: Option<f64>,
cmax: Option<f64>,
cmid: Option<f64>,
#[serde(rename = "colorscale")]
color_scale: Option<ColorScale>,
#[serde(rename = "autocolorscale")]
auto_color_scale: Option<bool>,
#[serde(rename = "reversescale")]
reverse_scale: Option<bool>,
#[serde(rename = "showscale")]
show_scale: Option<bool>,
#[serde(rename = "colorbar")]
color_bar: Option<ColorBar>,
}
impl ColorAxis {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum SpikeMode {
ToAxis,
Across,
Marker,
#[serde(rename = "toaxis+across")]
ToaxisAcross,
#[serde(rename = "toaxis+marker")]
ToAxisMarker,
#[serde(rename = "across+marker")]
AcrossMarker,
#[serde(rename = "toaxis+across+marker")]
ToaxisAcrossMarker,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum SpikeSnap {
Data,
Cursor,
#[serde(rename = "hovered data")]
HoveredData,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Axis {
visible: Option<bool>,
color: Option<Box<dyn Color>>,
title: Option<Title>,
#[field_setter(skip)]
r#type: Option<AxisType>,
#[serde(rename = "autorange")]
auto_range: Option<bool>,
#[serde(rename = "rangemode")]
range_mode: Option<RangeMode>,
range: Option<NumOrStringCollection>,
#[serde(rename = "fixedrange")]
fixed_range: Option<bool>,
constrain: Option<AxisConstrain>,
#[serde(rename = "constraintoward")]
constrain_toward: Option<ConstrainDirection>,
#[serde(rename = "tickmode")]
tick_mode: Option<TickMode>,
#[serde(rename = "nticks")]
n_ticks: Option<usize>,
tick0: Option<f64>,
dtick: Option<f64>,
#[field_setter(skip)]
matches: Option<String>,
#[serde(rename = "tickvals")]
tick_values: Option<Vec<f64>>,
#[serde(rename = "ticktext")]
tick_text: Option<Vec<String>>,
ticks: Option<TicksDirection>,
#[serde(rename = "tickson")]
ticks_on: Option<TicksPosition>,
mirror: Option<bool>,
#[serde(rename = "ticklen")]
tick_length: Option<usize>,
#[serde(rename = "tickwidth")]
tick_width: Option<usize>,
#[serde(rename = "tickcolor")]
tick_color: Option<Box<dyn Color>>,
#[serde(rename = "showticklabels")]
show_tick_labels: Option<bool>,
#[serde(rename = "automargin")]
auto_margin: Option<bool>,
#[serde(rename = "showspikes")]
show_spikes: Option<bool>,
#[serde(rename = "spikecolor")]
spike_color: Option<Box<dyn Color>>,
#[serde(rename = "spikethickness")]
spike_thickness: Option<usize>,
#[serde(rename = "spikedash")]
spike_dash: Option<DashType>,
#[serde(rename = "spikemode")]
spike_mode: Option<SpikeMode>,
#[serde(rename = "spikesnap")]
spike_snap: Option<SpikeSnap>,
#[serde(rename = "tickfont")]
tick_font: Option<Font>,
#[serde(rename = "tickangle")]
tick_angle: Option<f64>,
#[serde(rename = "tickprefix")]
tick_prefix: Option<String>,
#[serde(rename = "showtickprefix")]
show_tick_prefix: Option<ArrayShow>,
#[serde(rename = "ticksuffix")]
tick_suffix: Option<String>,
#[serde(rename = "showticksuffix")]
show_tick_suffix: Option<ArrayShow>,
#[serde(rename = "showexponent")]
show_exponent: Option<ArrayShow>,
#[serde(rename = "exponentformat")]
exponent_format: Option<ExponentFormat>,
#[serde(rename = "separatethousands")]
separate_thousands: Option<bool>,
#[serde(rename = "tickformat")]
tick_format: Option<String>,
#[serde(rename = "tickformatstops")]
tick_format_stops: Option<Vec<TickFormatStop>>,
#[serde(rename = "hoverformat")]
hover_format: Option<String>,
#[serde(rename = "showline")]
show_line: Option<bool>,
#[serde(rename = "linecolor")]
line_color: Option<Box<dyn Color>>,
#[serde(rename = "linewidth")]
line_width: Option<usize>,
#[serde(rename = "showgrid")]
show_grid: Option<bool>,
#[serde(rename = "gridcolor")]
grid_color: Option<Box<dyn Color>>,
#[serde(rename = "gridwidth")]
grid_width: Option<usize>,
#[serde(rename = "zeroline")]
zero_line: Option<bool>,
#[serde(rename = "zerolinecolor")]
zero_line_color: Option<Box<dyn Color>>,
#[serde(rename = "zerolinewidth")]
zero_line_width: Option<usize>,
#[serde(rename = "showdividers")]
show_dividers: Option<bool>,
#[serde(rename = "dividercolor")]
divider_color: Option<Box<dyn Color>>,
#[serde(rename = "dividerwidth")]
divider_width: Option<usize>,
anchor: Option<String>,
side: Option<AxisSide>,
overlaying: Option<String>,
#[field_setter(skip)]
domain: Option<Vec<f64>>,
position: Option<f64>,
#[serde(rename = "rangeslider")]
range_slider: Option<RangeSlider>,
#[serde(rename = "rangeselector")]
range_selector: Option<RangeSelector>,
calendar: Option<Calendar>,
}
impl Axis {
pub fn new() -> Self {
Default::default()
}
pub fn matches(mut self, matches: bool) -> Self {
if matches {
self.matches = Some(String::from("x"));
}
self
}
pub fn type_(mut self, t: AxisType) -> Self {
self.r#type = Some(t);
self
}
pub fn domain(mut self, domain: &[f64]) -> Self {
self.domain = Some(domain.to_vec());
self
}
}
#[derive(Serialize, Debug, Clone)]
pub enum RowOrder {
#[serde(rename = "top to bottom")]
TopToBottom,
#[serde(rename = "bottom to top")]
BottomToTop,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum GridPattern {
Independent,
Coupled,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum GridXSide {
Bottom,
#[serde(rename = "bottom plot")]
BottomPlot,
#[serde(rename = "top plot")]
TopPlot,
Top,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum GridYSide {
Left,
#[serde(rename = "left plot")]
LeftPlot,
#[serde(rename = "right plot")]
RightPlot,
Right,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct GridDomain {
x: Option<Vec<f64>>,
y: Option<Vec<f64>>,
}
impl GridDomain {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct LayoutGrid {
rows: Option<usize>,
#[serde(rename = "roworder")]
row_order: Option<RowOrder>,
columns: Option<usize>,
#[serde(rename = "subplots")]
sub_plots: Option<Vec<String>>,
#[serde(rename = "xaxes")]
x_axes: Option<Vec<String>>,
#[serde(rename = "yaxes")]
y_axes: Option<Vec<String>>,
pattern: Option<GridPattern>,
#[serde(rename = "xgap")]
x_gap: Option<f64>,
#[serde(rename = "ygap")]
y_gap: Option<f64>,
domain: Option<GridDomain>,
#[serde(rename = "xside")]
x_side: Option<GridXSide>,
#[serde(rename = "yside")]
y_side: Option<GridYSide>,
}
impl LayoutGrid {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Debug, Clone)]
pub enum UniformTextMode {
False,
Hide,
Show,
}
impl Serialize for UniformTextMode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Self::False => serializer.serialize_bool(false),
Self::Hide => serializer.serialize_str("hide"),
Self::Show => serializer.serialize_str("show"),
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct UniformText {
mode: Option<UniformTextMode>,
#[serde(rename = "minsize")]
min_size: Option<usize>,
}
impl UniformText {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Debug, Clone)]
pub enum HoverMode {
X,
Y,
Closest,
False,
XUnified,
YUnified,
}
impl Serialize for HoverMode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Self::X => serializer.serialize_str("x"),
Self::Y => serializer.serialize_str("y"),
Self::Closest => serializer.serialize_str("closest"),
Self::False => serializer.serialize_bool(false),
Self::XUnified => serializer.serialize_str("x unified"),
Self::YUnified => serializer.serialize_str("y unified"),
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct ModeBar {
orientation: Option<Orientation>,
#[serde(rename = "bgcolor")]
background_color: Option<Box<dyn Color>>,
color: Option<Box<dyn Color>>,
#[serde(rename = "activecolor")]
active_color: Option<Box<dyn Color>>,
}
impl ModeBar {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ShapeType {
Circle,
Rect,
Path,
Line,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ShapeLayer {
Below,
Above,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ShapeSizeMode {
Scaled,
Pixel,
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum FillRule {
EvenOdd,
NonZero,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct ShapeLine {
color: Option<Box<dyn Color>>,
width: Option<f64>,
dash: Option<DashType>,
}
impl ShapeLine {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Shape {
visible: Option<bool>,
#[field_setter(skip)]
r#type: Option<ShapeType>,
layer: Option<ShapeLayer>,
#[serde(rename = "xref")]
x_ref: Option<String>,
#[serde(rename = "xsizemode")]
x_size_mode: Option<ShapeSizeMode>,
#[serde(rename = "xanchor")]
x_anchor: Option<NumOrString>,
x0: Option<NumOrString>,
x1: Option<NumOrString>,
#[serde(rename = "yref")]
y_ref: Option<String>,
#[serde(rename = "ysizemode")]
y_size_mode: Option<ShapeSizeMode>,
#[serde(rename = "yanchor")]
y_anchor: Option<NumOrString>,
y0: Option<NumOrString>,
y1: Option<NumOrString>,
path: Option<String>,
opacity: Option<f64>,
line: Option<ShapeLine>,
#[serde(rename = "fillcolor")]
fill_color: Option<Box<dyn Color>>,
#[serde(rename = "fillrule")]
fill_rule: Option<FillRule>,
editable: Option<bool>,
name: Option<String>,
#[serde(rename = "templateitemname")]
template_item_name: Option<String>,
}
impl Shape {
pub fn new() -> Self {
Default::default()
}
pub fn shape_type(mut self, shape_type: ShapeType) -> Self {
self.r#type = Some(shape_type);
self
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum DrawDirection {
Ortho,
Horizontal,
Vertical,
Diagonal,
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct NewShape {
line: Option<ShapeLine>,
#[serde(rename = "fillcolor")]
fill_color: Option<Box<dyn Color>>,
#[serde(rename = "fillrule")]
fill_rule: Option<FillRule>,
opacity: Option<f64>,
layer: Option<ShapeLayer>,
#[serde(rename = "drawdirection")]
draw_direction: Option<DrawDirection>,
}
impl NewShape {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct ActiveShape {
#[serde(rename = "fillcolor")]
fill_color: Option<Box<dyn Color>>,
opacity: Option<f64>,
}
impl ActiveShape {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ArrowSide {
End,
Start,
#[serde(rename = "end+start")]
StartEnd,
None,
}
#[derive(Debug, Clone)]
pub enum ClickToShow {
False,
OnOff,
OnOut,
}
impl Serialize for ClickToShow {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
match *self {
Self::False => serializer.serialize_bool(false),
Self::OnOff => serializer.serialize_str("onoff"),
Self::OnOut => serializer.serialize_str("onout"),
}
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Annotation {
visible: Option<bool>,
text: Option<String>,
#[serde(rename = "textangle")]
text_angle: Option<f64>,
font: Option<Font>,
width: Option<f64>,
height: Option<f64>,
opacity: Option<f64>,
align: Option<HAlign>,
valign: Option<VAlign>,
#[serde(rename = "bgcolor")]
background_color: Option<Box<dyn Color>>,
#[serde(rename = "bordercolor")]
border_color: Option<Box<dyn Color>>,
#[serde(rename = "borderpad")]
border_pad: Option<f64>,
#[serde(rename = "borderwidth")]
border_width: Option<f64>,
#[serde(rename = "showarrow")]
show_arrow: Option<bool>,
#[serde(rename = "arrowcolor")]
arrow_color: Option<Box<dyn Color>>,
#[serde(rename = "arrowhead")]
arrow_head: Option<u8>,
#[serde(rename = "startarrowhead")]
start_arrow_head: Option<u8>,
#[serde(rename = "arrowside")]
arrow_side: Option<ArrowSide>,
#[serde(rename = "arrowsize")]
arrow_size: Option<f64>,
#[serde(rename = "startarrowsize")]
start_arrow_size: Option<f64>,
#[serde(rename = "arrowwidth")]
arrow_width: Option<f64>,
#[serde(rename = "standoff")]
stand_off: Option<f64>,
#[serde(rename = "startstandoff")]
start_stand_off: Option<f64>,
ax: Option<NumOrString>,
ay: Option<NumOrString>,
#[serde(rename = "axref")]
ax_ref: Option<String>,
#[serde(rename = "ayref")]
ay_ref: Option<String>,
#[serde(rename = "xref")]
x_ref: Option<String>,
x: Option<NumOrString>,
#[serde(rename = "xanchor")]
x_anchor: Option<Anchor>,
#[serde(rename = "xshift")]
x_shift: Option<f64>,
#[serde(rename = "yref")]
y_ref: Option<String>,
y: Option<NumOrString>,
#[serde(rename = "yanchor")]
y_anchor: Option<Anchor>,
#[serde(rename = "yshift")]
y_shift: Option<f64>,
#[serde(rename = "clicktoshow")]
click_to_show: Option<ClickToShow>,
#[serde(rename = "xclick")]
x_click: Option<NumOrString>,
#[serde(rename = "yclick")]
y_click: Option<NumOrString>,
#[serde(rename = "hovertext")]
hover_text: Option<String>,
#[serde(rename = "hoverlabel")]
hover_label: Option<Label>,
#[serde(rename = "captureevents")]
capture_events: Option<bool>,
name: Option<String>,
#[serde(rename = "templateitemname")]
template_item_name: Option<String>,
}
impl Annotation {
pub fn new() -> Self {
Default::default()
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum ClickMode {
Event,
Select,
#[serde(rename = "event+select")]
EventAndSelect,
None,
}
#[derive(Debug, Clone)]
pub enum DragMode {
Zoom,
Pan,
Select,
Lasso,
DrawClosedPath,
DrawOpenPath,
DrawLine,
DrawRect,
DrawCircle,
Orbit,
Turntable,
False,
}
impl Serialize for DragMode {
fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
where
S: serde::Serializer,
{
match *self {
Self::Zoom => serializer.serialize_str("zoom"),
Self::Pan => serializer.serialize_str("pan"),
Self::Select => serializer.serialize_str("select"),
Self::Lasso => serializer.serialize_str("lasso"),
Self::DrawClosedPath => serializer.serialize_str("drawclosedpath"),
Self::DrawOpenPath => serializer.serialize_str("drawopenpath"),
Self::DrawLine => serializer.serialize_str("drawline"),
Self::DrawRect => serializer.serialize_str("drawrect"),
Self::DrawCircle => serializer.serialize_str("drawcircle"),
Self::Orbit => serializer.serialize_str("orbit"),
Self::Turntable => serializer.serialize_str("turntable"),
Self::False => serializer.serialize_bool(false),
}
}
}
#[derive(Serialize, Debug, Clone)]
#[serde(rename_all = "lowercase")]
pub enum SelectDirection {
#[serde(rename = "h")]
Horizontal,
#[serde(rename = "v")]
Vertical,
#[serde(rename = "d")]
Diagonal,
Any,
}
#[derive(Serialize, Clone, Debug)]
pub struct Center {
lat: f64,
lon: f64,
}
impl Center {
pub fn new(lat: f64, lon: f64) -> Self {
Center { lat, lon }
}
}
#[derive(Serialize, Clone, Debug)]
#[serde(rename_all = "kebab-case")]
pub enum MapboxStyle {
#[serde(rename = "carto-darkmatter")]
CartoDarkMatter,
CartoPositron,
OpenStreetMap,
StamenTerrain,
StamenToner,
StamenWatercolor,
WhiteBg,
Basic,
Streets,
Outdoors,
Light,
Dark,
Satellite,
SatelliteStreets,
}
#[derive(Serialize, Clone, Debug, FieldSetter)]
pub struct Mapbox {
#[serde(rename = "accesstoken")]
access_token: Option<String>,
bearing: Option<f64>,
center: Option<Center>,
pitch: Option<f64>,
style: Option<MapboxStyle>,
zoom: Option<u8>,
}
impl Mapbox {
pub fn new() -> Self {
Default::default()
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct Template {
layout: Option<LayoutTemplate>,
}
impl Template {
pub fn new() -> Self {
Default::default()
}
}
#[allow(clippy::from_over_into)]
impl Into<Cow<'static, Template>> for Template {
fn into(self) -> Cow<'static, Template> {
Cow::Owned(self)
}
}
#[allow(clippy::from_over_into)]
impl Into<Cow<'static, Template>> for &'static Template {
fn into(self) -> Cow<'static, Template> {
Cow::Borrowed(self)
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
pub struct LayoutTemplate {
title: Option<Title>,
#[serde(rename = "showlegend")]
show_legend: Option<bool>,
legend: Option<Legend>,
margin: Option<Margin>,
#[serde(rename = "autosize")]
auto_size: Option<bool>,
width: Option<usize>,
height: Option<usize>,
font: Option<Font>,
#[serde(rename = "uniformtext")]
uniform_text: Option<UniformText>,
separators: Option<String>,
#[serde(rename = "paper_bgcolor")]
paper_background_color: Option<Box<dyn Color>>,
#[serde(rename = "plot_bgcolor")]
plot_background_color: Option<Box<dyn Color>>,
#[serde(rename = "colorscale")]
color_scale: Option<LayoutColorScale>,
colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "coloraxis")]
color_axis: Option<ColorAxis>,
#[serde(rename = "modebar")]
mode_bar: Option<ModeBar>,
#[serde(rename = "hovermode")]
hover_mode: Option<HoverMode>,
#[serde(rename = "clickmode")]
click_mode: Option<ClickMode>,
#[serde(rename = "dragmode")]
drag_mode: Option<DragMode>,
#[serde(rename = "selectdirection")]
select_direction: Option<SelectDirection>,
#[serde(rename = "hoverdistance")]
hover_distance: Option<i32>,
#[serde(rename = "spikedistance")]
spike_distance: Option<i32>,
#[serde(rename = "hoverlabel")]
hover_label: Option<Label>,
grid: Option<LayoutGrid>,
calendar: Option<Calendar>,
#[serde(rename = "xaxis")]
x_axis: Option<Box<Axis>>,
#[serde(rename = "yaxis")]
y_axis: Option<Box<Axis>>,
#[serde(rename = "xaxis2")]
x_axis2: Option<Box<Axis>>,
#[serde(rename = "yaxis2")]
y_axis2: Option<Box<Axis>>,
#[serde(rename = "xaxis3")]
x_axis3: Option<Box<Axis>>,
#[serde(rename = "yaxis3")]
y_axis3: Option<Box<Axis>>,
#[serde(rename = "xaxis4")]
x_axis4: Option<Box<Axis>>,
#[serde(rename = "yaxis4")]
y_axis4: Option<Box<Axis>>,
#[serde(rename = "xaxis5")]
x_axis5: Option<Box<Axis>>,
#[serde(rename = "yaxis5")]
y_axis5: Option<Box<Axis>>,
#[serde(rename = "xaxis6")]
x_axis6: Option<Box<Axis>>,
#[serde(rename = "yaxis6")]
y_axis6: Option<Box<Axis>>,
#[serde(rename = "xaxis7")]
x_axis7: Option<Box<Axis>>,
#[serde(rename = "yaxis7")]
y_axis7: Option<Box<Axis>>,
#[serde(rename = "xaxis8")]
x_axis8: Option<Box<Axis>>,
#[serde(rename = "yaxis8")]
y_axis8: Option<Box<Axis>>,
annotations: Option<Vec<Annotation>>,
shapes: Option<Vec<Shape>>,
#[serde(rename = "newshape")]
new_shape: Option<NewShape>,
#[serde(rename = "activeshape")]
active_shape: Option<ActiveShape>,
#[serde(rename = "boxmode")]
box_mode: Option<BoxMode>,
#[serde(rename = "boxgap")]
box_gap: Option<f64>,
#[serde(rename = "boxgroupgap")]
box_group_gap: Option<f64>,
#[serde(rename = "barmode")]
bar_mode: Option<BarMode>,
#[serde(rename = "barnorm")]
bar_norm: Option<BarNorm>,
#[serde(rename = "bargap")]
bar_gap: Option<f64>,
#[serde(rename = "bargroupgap")]
bar_group_gap: Option<f64>,
#[serde(rename = "violinmode")]
violin_mode: Option<ViolinMode>,
#[serde(rename = "violingap")]
violin_gap: Option<f64>,
#[serde(rename = "violingroupgap")]
violin_group_gap: Option<f64>,
#[serde(rename = "waterfallmode")]
waterfall_mode: Option<WaterfallMode>,
#[serde(rename = "waterfallgap")]
waterfall_gap: Option<f64>,
#[serde(rename = "waterfallgroupgap")]
waterfall_group_gap: Option<f64>,
#[serde(rename = "piecolorway")]
pie_colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "extendpiecolors")]
extend_pie_colors: Option<bool>,
#[serde(rename = "sunburstcolorway")]
sunburst_colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "extendsunburstcolors")]
extend_sunburst_colors: Option<bool>,
}
impl LayoutTemplate {
pub fn new() -> Self {
Default::default()
}
pub fn add_annotation(&mut self, annotation: Annotation) {
if self.annotations.is_none() {
self.annotations = Some(Vec::new());
}
self.annotations.as_mut().unwrap().push(annotation);
}
pub fn add_shape(&mut self, shape: Shape) {
if self.shapes.is_none() {
self.shapes = Some(Vec::new());
}
self.shapes.as_mut().unwrap().push(shape);
}
}
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
#[field_setter(kind = "layout")]
pub struct Layout {
title: Option<Title>,
#[serde(rename = "showlegend")]
show_legend: Option<bool>,
legend: Option<Legend>,
margin: Option<Margin>,
#[serde(rename = "autosize")]
auto_size: Option<bool>,
width: Option<usize>,
height: Option<usize>,
font: Option<Font>,
#[serde(rename = "uniformtext")]
uniform_text: Option<UniformText>,
separators: Option<String>,
#[serde(rename = "paper_bgcolor")]
paper_background_color: Option<Box<dyn Color>>,
#[serde(rename = "plot_bgcolor")]
plot_background_color: Option<Box<dyn Color>>,
#[serde(rename = "colorscale")]
color_scale: Option<LayoutColorScale>,
colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "coloraxis")]
color_axis: Option<ColorAxis>,
#[serde(rename = "modebar")]
mode_bar: Option<ModeBar>,
#[serde(rename = "hovermode")]
hover_mode: Option<HoverMode>,
#[serde(rename = "clickmode")]
click_mode: Option<ClickMode>,
#[serde(rename = "dragmode")]
drag_mode: Option<DragMode>,
#[serde(rename = "selectdirection")]
select_direction: Option<SelectDirection>,
#[serde(rename = "hoverdistance")]
hover_distance: Option<i32>,
#[serde(rename = "spikedistance")]
spike_distance: Option<i32>,
#[serde(rename = "hoverlabel")]
hover_label: Option<Label>,
#[field_setter(skip)]
template: Option<Box<Cow<'static, Template>>>,
grid: Option<LayoutGrid>,
calendar: Option<Calendar>,
#[serde(rename = "xaxis")]
x_axis: Option<Box<Axis>>,
#[serde(rename = "yaxis")]
y_axis: Option<Box<Axis>>,
#[serde(rename = "zaxis")]
z_axis: Option<Box<Axis>>,
#[serde(rename = "xaxis2")]
x_axis2: Option<Box<Axis>>,
#[serde(rename = "yaxis2")]
y_axis2: Option<Box<Axis>>,
#[serde(rename = "zaxis2")]
z_axis2: Option<Box<Axis>>,
#[serde(rename = "xaxis3")]
x_axis3: Option<Box<Axis>>,
#[serde(rename = "yaxis3")]
y_axis3: Option<Box<Axis>>,
#[serde(rename = "zaxis3")]
z_axis3: Option<Box<Axis>>,
#[serde(rename = "xaxis4")]
x_axis4: Option<Box<Axis>>,
#[serde(rename = "yaxis4")]
y_axis4: Option<Box<Axis>>,
#[serde(rename = "zaxis4")]
z_axis4: Option<Box<Axis>>,
#[serde(rename = "xaxis5")]
x_axis5: Option<Box<Axis>>,
#[serde(rename = "yaxis5")]
y_axis5: Option<Box<Axis>>,
#[serde(rename = "zaxis5")]
z_axis5: Option<Box<Axis>>,
#[serde(rename = "xaxis6")]
x_axis6: Option<Box<Axis>>,
#[serde(rename = "yaxis6")]
y_axis6: Option<Box<Axis>>,
#[serde(rename = "zaxis6")]
z_axis6: Option<Box<Axis>>,
#[serde(rename = "xaxis7")]
x_axis7: Option<Box<Axis>>,
#[serde(rename = "yaxis7")]
y_axis7: Option<Box<Axis>>,
#[serde(rename = "zaxis7")]
z_axis7: Option<Box<Axis>>,
#[serde(rename = "xaxis8")]
x_axis8: Option<Box<Axis>>,
#[serde(rename = "yaxis8")]
y_axis8: Option<Box<Axis>>,
#[serde(rename = "zaxis8")]
z_axis8: Option<Box<Axis>>,
annotations: Option<Vec<Annotation>>,
shapes: Option<Vec<Shape>>,
#[serde(rename = "newshape")]
new_shape: Option<NewShape>,
#[serde(rename = "activeshape")]
active_shape: Option<ActiveShape>,
#[serde(rename = "boxmode")]
box_mode: Option<BoxMode>,
#[serde(rename = "boxgap")]
box_gap: Option<f64>,
#[serde(rename = "boxgroupgap")]
box_group_gap: Option<f64>,
#[serde(rename = "barmode")]
bar_mode: Option<BarMode>,
#[serde(rename = "barnorm")]
bar_norm: Option<BarNorm>,
#[serde(rename = "bargap")]
bar_gap: Option<f64>,
#[serde(rename = "bargroupgap")]
bar_group_gap: Option<f64>,
#[serde(rename = "violinmode")]
violin_mode: Option<ViolinMode>,
#[serde(rename = "violingap")]
violin_gap: Option<f64>,
#[serde(rename = "violingroupgap")]
violin_group_gap: Option<f64>,
#[serde(rename = "waterfallmode")]
waterfall_mode: Option<WaterfallMode>,
#[serde(rename = "waterfallgap")]
waterfall_gap: Option<f64>,
#[serde(rename = "waterfallgroupgap")]
waterfall_group_gap: Option<f64>,
#[serde(rename = "piecolorway")]
pie_colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "extendpiecolors")]
extend_pie_colors: Option<bool>,
#[serde(rename = "sunburstcolorway")]
sunburst_colorway: Option<Vec<Box<dyn Color>>>,
#[serde(rename = "extendsunburstcolors")]
extend_sunburst_colors: Option<bool>,
mapbox: Option<Mapbox>,
#[serde(rename = "updatemenus")]
update_menus: Option<Vec<UpdateMenu>>,
}
impl Layout {
pub fn new() -> Self {
Default::default()
}
pub fn to_json(&self) -> String {
serde_json::to_string(self).unwrap()
}
pub fn add_annotation(&mut self, annotation: Annotation) {
if self.annotations.is_none() {
self.annotations = Some(Vec::new());
}
self.annotations.as_mut().unwrap().push(annotation);
}
pub fn add_shape(&mut self, shape: Shape) {
if self.shapes.is_none() {
self.shapes = Some(Vec::new());
}
self.shapes.as_mut().unwrap().push(shape);
}
pub fn template<T>(mut self, template: T) -> Layout
where
T: Into<Cow<'static, Template>>,
{
self.template = Some(Box::new(template.into()));
self
}
}
#[cfg(test)]
mod tests {
use serde_json::{json, to_value};
use super::*;
use crate::common::ColorScalePalette;
#[test]
fn test_serialize_uniform_text_mode() {
assert_eq!(to_value(UniformTextMode::False).unwrap(), json!(false));
assert_eq!(to_value(UniformTextMode::Hide).unwrap(), json!("hide"));
assert_eq!(to_value(UniformTextMode::Show).unwrap(), json!("show"));
}
#[test]
fn test_serialize_click_to_show() {
assert_eq!(to_value(ClickToShow::False).unwrap(), json!(false));
assert_eq!(to_value(ClickToShow::OnOff).unwrap(), json!("onoff"));
assert_eq!(to_value(ClickToShow::OnOut).unwrap(), json!("onout"));
}
#[test]
fn test_serialize_hover_mode() {
assert_eq!(to_value(HoverMode::X).unwrap(), json!("x"));
assert_eq!(to_value(HoverMode::Y).unwrap(), json!("y"));
assert_eq!(to_value(HoverMode::Closest).unwrap(), json!("closest"));
assert_eq!(to_value(HoverMode::False).unwrap(), json!(false));
assert_eq!(to_value(HoverMode::XUnified).unwrap(), json!("x unified"));
assert_eq!(to_value(HoverMode::YUnified).unwrap(), json!("y unified"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_axis_type() {
assert_eq!(to_value(AxisType::Default).unwrap(), json!("-"));
assert_eq!(to_value(AxisType::Linear).unwrap(), json!("linear"));
assert_eq!(to_value(AxisType::Log).unwrap(), json!("log"));
assert_eq!(to_value(AxisType::Date).unwrap(), json!("date"));
assert_eq!(to_value(AxisType::Category).unwrap(), json!("category"));
assert_eq!(to_value(AxisType::MultiCategory).unwrap(), json!("multicategory"));
}
#[test]
fn test_serialize_axis_constrain() {
assert_eq!(to_value(AxisConstrain::Range).unwrap(), json!("range"));
assert_eq!(to_value(AxisConstrain::Domain).unwrap(), json!("domain"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_constrain_direction() {
assert_eq!(to_value(ConstrainDirection::Left).unwrap(), json!("left"));
assert_eq!(to_value(ConstrainDirection::Center).unwrap(), json!("center"));
assert_eq!(to_value(ConstrainDirection::Right).unwrap(), json!("right"));
assert_eq!(to_value(ConstrainDirection::Top).unwrap(), json!("top"));
assert_eq!(to_value(ConstrainDirection::Middle).unwrap(), json!("middle"));
assert_eq!(to_value(ConstrainDirection::Bottom).unwrap(), json!("bottom"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_range_mode() {
assert_eq!(to_value(RangeMode::Normal).unwrap(), json!("normal"));
assert_eq!(to_value(RangeMode::ToZero).unwrap(), json!("tozero"));
assert_eq!(to_value(RangeMode::NonNegative).unwrap(), json!("nonnegative"));
}
#[test]
fn test_serialize_ticks_direction() {
assert_eq!(to_value(TicksDirection::Outside).unwrap(), json!("outside"));
assert_eq!(to_value(TicksDirection::Inside).unwrap(), json!("inside"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_ticks_position() {
assert_eq!(to_value(TicksPosition::Labels).unwrap(), json!("labels"));
assert_eq!(to_value(TicksPosition::Boundaries).unwrap(), json!("boundaries"));
}
#[test]
fn test_serialize_array_show() {
assert_eq!(to_value(ArrayShow::All).unwrap(), json!("all"));
assert_eq!(to_value(ArrayShow::First).unwrap(), json!("first"));
assert_eq!(to_value(ArrayShow::Last).unwrap(), json!("last"));
assert_eq!(to_value(ArrayShow::None).unwrap(), json!("none"));
}
#[test]
fn test_serialize_bar_mode() {
assert_eq!(to_value(BarMode::Stack).unwrap(), json!("stack"));
assert_eq!(to_value(BarMode::Group).unwrap(), json!("group"));
assert_eq!(to_value(BarMode::Overlay).unwrap(), json!("overlay"));
assert_eq!(to_value(BarMode::Relative).unwrap(), json!("relative"));
}
#[test]
fn test_serialize_bar_norm() {
assert_eq!(to_value(BarNorm::Empty).unwrap(), json!(""));
assert_eq!(to_value(BarNorm::Fraction).unwrap(), json!("fraction"));
assert_eq!(to_value(BarNorm::Percent).unwrap(), json!("percent"));
}
#[test]
fn test_serialize_box_mode() {
assert_eq!(to_value(BoxMode::Group).unwrap(), json!("group"));
assert_eq!(to_value(BoxMode::Overlay).unwrap(), json!("overlay"));
}
#[test]
fn test_serialize_violin_mode() {
assert_eq!(to_value(ViolinMode::Group).unwrap(), json!("group"));
assert_eq!(to_value(ViolinMode::Overlay).unwrap(), json!("overlay"));
}
#[test]
fn test_serialize_waterfall_mode() {
assert_eq!(to_value(WaterfallMode::Group).unwrap(), json!("group"));
assert_eq!(to_value(WaterfallMode::Overlay).unwrap(), json!("overlay"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_trace_order() {
assert_eq!(to_value(TraceOrder::Reversed).unwrap(), json!("reversed"));
assert_eq!(to_value(TraceOrder::Grouped).unwrap(), json!("grouped"));
assert_eq!(to_value(TraceOrder::ReversedGrouped).unwrap(), json!("reversed+grouped"));
assert_eq!(to_value(TraceOrder::Normal).unwrap(), json!("normal"));
}
#[test]
fn test_serialize_item_sizing() {
assert_eq!(to_value(ItemSizing::Trace).unwrap(), json!("trace"));
assert_eq!(to_value(ItemSizing::Constant).unwrap(), json!("constant"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_item_click() {
assert_eq!(to_value(ItemClick::Toggle).unwrap(), json!("toggle"));
assert_eq!(to_value(ItemClick::ToggleOthers).unwrap(), json!("toggleothers"));
assert_eq!(to_value(ItemClick::False).unwrap(), json!(false));
}
#[test]
#[rustfmt::skip]
fn test_serialize_group_click() {
assert_eq!(to_value(GroupClick::ToggleItem).unwrap(), json!("toggleitem"));
assert_eq!(to_value(GroupClick::ToggleGroup).unwrap(), json!("togglegroup"));
}
#[test]
fn test_serialize_legend() {
let legend = Legend::new()
.background_color("#123123")
.border_color("#321321")
.border_width(500)
.font(Font::new())
.orientation(Orientation::Vertical)
.trace_order(TraceOrder::Normal)
.trace_group_gap(10)
.item_sizing(ItemSizing::Trace)
.item_click(ItemClick::Toggle)
.item_double_click(ItemClick::False)
.x(1.0)
.x_anchor(Anchor::Auto)
.y(2.0)
.y_anchor(Anchor::Left)
.valign(VAlign::Middle)
.title(Title::new("title"))
.group_click(GroupClick::ToggleItem)
.item_width(50);
let expected = json!({
"bgcolor": "#123123",
"bordercolor": "#321321",
"borderwidth": 500,
"font": {},
"orientation": "v",
"traceorder": "normal",
"tracegroupgap": 10,
"itemsizing": "trace",
"itemclick": "toggle",
"itemdoubleclick": false,
"x": 1.0,
"xanchor": "auto",
"y": 2.0,
"yanchor": "left",
"valign": "middle",
"title": {"text": "title"},
"groupclick": "toggleitem",
"itemwidth": 50
});
assert_eq!(to_value(legend).unwrap(), expected)
}
#[test]
fn test_serialize_valign() {
assert_eq!(to_value(VAlign::Top).unwrap(), json!("top"));
assert_eq!(to_value(VAlign::Middle).unwrap(), json!("middle"));
assert_eq!(to_value(VAlign::Bottom).unwrap(), json!("bottom"));
}
#[test]
fn test_serialize_halign() {
assert_eq!(to_value(HAlign::Left).unwrap(), json!("left"));
assert_eq!(to_value(HAlign::Center).unwrap(), json!("center"));
assert_eq!(to_value(HAlign::Right).unwrap(), json!("right"));
}
#[test]
fn test_serialize_margin() {
let margin = Margin::new()
.left(1)
.right(2)
.top(3)
.bottom(4)
.pad(5)
.auto_expand(true);
let expected = json!({
"l": 1,
"r": 2,
"t": 3,
"b": 4,
"pad": 5,
"autoexpand": true,
});
assert_eq!(to_value(margin).unwrap(), expected);
}
#[test]
fn test_serialize_layout_color_scale() {
let layout_color_scale = LayoutColorScale::new()
.sequential(ColorScale::Palette(ColorScalePalette::Greys))
.sequential_minus(ColorScale::Palette(ColorScalePalette::Blues))
.diverging(ColorScale::Palette(ColorScalePalette::Hot));
let expected = json!({
"sequential": "Greys",
"sequentialminus": "Blues",
"diverging": "Hot"
});
assert_eq!(to_value(layout_color_scale).unwrap(), expected);
}
#[test]
fn test_serialize_slider_range_mode() {
assert_eq!(to_value(SliderRangeMode::Auto).unwrap(), json!("auto"));
assert_eq!(to_value(SliderRangeMode::Fixed).unwrap(), json!("fixed"));
assert_eq!(to_value(SliderRangeMode::Match).unwrap(), json!("match"));
}
#[test]
fn test_serialize_range_slider_y_axis() {
let range_slider_y_axis = RangeSliderYAxis::new()
.range_mode(SliderRangeMode::Match)
.range(vec![0.2]);
let expected = json!({
"rangemode": "match",
"range": [0.2]
});
assert_eq!(to_value(range_slider_y_axis).unwrap(), expected);
}
#[test]
fn test_serialize_range_slider() {
let range_slider = RangeSlider::new()
.background_color("#123ABC")
.border_color("#ABC123")
.border_width(1000)
.auto_range(false)
.range(vec![5_i32])
.thickness(2000.)
.visible(true)
.y_axis(RangeSliderYAxis::new());
let expected = json!({
"bgcolor": "#123ABC",
"bordercolor": "#ABC123",
"borderwidth": 1000,
"autorange": false,
"range": [5],
"thickness": 2000.0,
"visible": true,
"yaxis": {}
});
assert_eq!(to_value(range_slider).unwrap(), expected);
}
#[test]
fn test_serialize_selector_step() {
assert_eq!(to_value(SelectorStep::Month).unwrap(), json!("month"));
assert_eq!(to_value(SelectorStep::Year).unwrap(), json!("year"));
assert_eq!(to_value(SelectorStep::Day).unwrap(), json!("day"));
assert_eq!(to_value(SelectorStep::Hour).unwrap(), json!("hour"));
assert_eq!(to_value(SelectorStep::Minute).unwrap(), json!("minute"));
assert_eq!(to_value(SelectorStep::Second).unwrap(), json!("second"));
assert_eq!(to_value(SelectorStep::All).unwrap(), json!("all"));
}
#[test]
fn test_serialize_step_mode() {
assert_eq!(to_value(StepMode::Backward).unwrap(), json!("backward"));
assert_eq!(to_value(StepMode::ToDate).unwrap(), json!("todate"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_spike_mode() {
assert_eq!(to_value(SpikeMode::ToAxis).unwrap(), json!("toaxis"));
assert_eq!(to_value(SpikeMode::Across).unwrap(), json!("across"));
assert_eq!(to_value(SpikeMode::Marker).unwrap(), json!("marker"));
assert_eq!(to_value(SpikeMode::ToaxisAcross).unwrap(), json!("toaxis+across"));
assert_eq!(to_value(SpikeMode::ToAxisMarker).unwrap(), json!("toaxis+marker"));
assert_eq!(to_value(SpikeMode::AcrossMarker).unwrap(), json!("across+marker"));
assert_eq!(to_value(SpikeMode::ToaxisAcrossMarker).unwrap(), json!("toaxis+across+marker"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_spike_snap() {
assert_eq!(to_value(SpikeSnap::Data).unwrap(), json!("data"));
assert_eq!(to_value(SpikeSnap::Cursor).unwrap(), json!("cursor"));
assert_eq!(to_value(SpikeSnap::HoveredData).unwrap(), json!("hovered data"));
}
#[test]
fn test_serialize_selector_button() {
let selector_button = SelectorButton::new()
.visible(false)
.step(SelectorStep::Hour)
.step_mode(StepMode::ToDate)
.count(42)
.label("label")
.name("name")
.template_item_name("something");
let expected = json!({
"visible": false,
"step": "hour",
"stepmode": "todate",
"count": 42,
"label": "label",
"name": "name",
"templateitemname": "something",
});
assert_eq!(to_value(selector_button).unwrap(), expected);
}
#[test]
fn test_serialize_range_selector() {
let range_selector = RangeSelector::new()
.visible(true)
.buttons(vec![SelectorButton::new()])
.x(2.0)
.x_anchor(Anchor::Middle)
.y(4.0)
.y_anchor(Anchor::Top)
.font(Font::new())
.background_color("#123ABC")
.border_color("#ABC123")
.border_width(1000)
.active_color("#888999");
let expected = json!({
"visible": true,
"buttons": [{}],
"x": 2.0,
"xanchor": "middle",
"y": 4.0,
"yanchor": "top",
"font": {},
"bgcolor": "#123ABC",
"bordercolor": "#ABC123",
"borderwidth": 1000,
"activecolor": "#888999",
});
assert_eq!(to_value(range_selector).unwrap(), expected);
}
#[test]
fn test_serialize_color_axis() {
let color_axis = ColorAxis::new()
.auto_color_scale(false)
.cauto(true)
.cmax(1.0)
.cmid(0.5)
.cmin(0.0)
.color_bar(ColorBar::new())
.color_scale(ColorScale::Palette(ColorScalePalette::Greens))
.reverse_scale(false)
.show_scale(true);
let expected = json!({
"autocolorscale": false,
"cauto": true,
"cmin": 0.0,
"cmid": 0.5,
"cmax": 1.0,
"colorbar": {},
"colorscale": "Greens",
"reversescale": false,
"showscale": true,
});
assert_eq!(to_value(color_axis).unwrap(), expected);
}
#[test]
fn test_serialize_axis() {
let axis = Axis::new()
.visible(false)
.color("#678123")
.title(Title::new("title"))
.type_(AxisType::Date)
.auto_range(false)
.range_mode(RangeMode::NonNegative)
.range(vec![2.0])
.fixed_range(true)
.constrain(AxisConstrain::Range)
.constrain_toward(ConstrainDirection::Middle)
.tick_mode(TickMode::Auto)
.n_ticks(600)
.tick0(5.0)
.dtick(10.0)
.matches(true)
.tick_values(vec![1.0, 2.0])
.tick_text(vec!["one".to_string(), "two".to_string()])
.ticks(TicksDirection::Inside)
.ticks_on(TicksPosition::Boundaries)
.mirror(false)
.tick_length(77)
.tick_width(99)
.tick_color("#101010")
.show_tick_labels(false)
.auto_margin(true)
.show_spikes(false)
.spike_color("#ABABAB")
.spike_thickness(501)
.spike_dash(DashType::DashDot)
.spike_mode(SpikeMode::AcrossMarker)
.spike_snap(SpikeSnap::Data)
.tick_font(Font::new())
.tick_angle(2.1)
.tick_prefix("prefix")
.show_tick_prefix(ArrayShow::Last)
.tick_suffix("suffix")
.show_tick_suffix(ArrayShow::None)
.show_exponent(ArrayShow::All)
.exponent_format(ExponentFormat::SmallE)
.separate_thousands(false)
.tick_format("tickfmt")
.tick_format_stops(vec![TickFormatStop::new()])
.hover_format("hoverfmt")
.show_line(true)
.line_color("#CCCDDD")
.line_width(9)
.show_grid(false)
.grid_color("#fff000")
.grid_width(8)
.zero_line(true)
.zero_line_color("#f0f0f0")
.zero_line_width(7)
.show_dividers(false)
.divider_color("#AFAFAF")
.divider_width(55)
.anchor("anchor")
.side(AxisSide::Right)
.overlaying("overlaying")
.domain(&[0.0, 1.0])
.position(0.6)
.range_slider(RangeSlider::new())
.range_selector(RangeSelector::new())
.calendar(Calendar::Coptic);
let expected = json!({
"visible": false,
"color": "#678123",
"title": {"text": "title"},
"type": "date",
"autorange": false,
"rangemode": "nonnegative",
"range": [2.0],
"fixedrange": true,
"constrain": "range",
"constraintoward": "middle",
"tickmode": "auto",
"nticks": 600,
"tick0": 5.0,
"dtick": 10.0,
"matches": "x",
"tickvals": [1.0, 2.0],
"ticktext": ["one", "two"],
"ticks": "inside",
"tickson": "boundaries",
"mirror": false,
"ticklen": 77,
"tickwidth": 99,
"tickcolor": "#101010",
"showticklabels": false,
"automargin": true,
"showspikes": false,
"spikecolor": "#ABABAB",
"spikethickness": 501,
"spikedash": "dashdot",
"spikemode": "across+marker",
"spikesnap": "data",
"tickfont": {},
"tickangle": 2.1,
"tickprefix": "prefix",
"showtickprefix": "last",
"ticksuffix": "suffix",
"showticksuffix": "none",
"showexponent": "all",
"exponentformat": "e",
"separatethousands": false,
"tickformat": "tickfmt",
"tickformatstops": [{"enabled": true}],
"hoverformat": "hoverfmt",
"showline": true,
"linecolor": "#CCCDDD",
"linewidth": 9,
"showgrid": false,
"gridcolor": "#fff000",
"gridwidth": 8,
"zeroline": true,
"zerolinecolor": "#f0f0f0",
"zerolinewidth": 7,
"showdividers": false,
"dividercolor": "#AFAFAF",
"dividerwidth": 55,
"anchor": "anchor",
"side": "right",
"overlaying": "overlaying",
"domain": [0.0, 1.0],
"position": 0.6,
"rangeslider": {},
"rangeselector": {},
"calendar": "coptic",
});
assert_eq!(to_value(axis).unwrap(), expected);
}
#[test]
#[rustfmt::skip]
fn test_serialize_row_order() {
assert_eq!(to_value(RowOrder::TopToBottom).unwrap(), json!("top to bottom"));
assert_eq!(to_value(RowOrder::BottomToTop).unwrap(), json!("bottom to top"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_grid_pattern() {
assert_eq!(to_value(GridPattern::Independent).unwrap(), json!("independent"));
assert_eq!(to_value(GridPattern::Coupled).unwrap(), json!("coupled"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_grid_x_side() {
assert_eq!(to_value(GridXSide::Bottom).unwrap(), json!("bottom"));
assert_eq!(to_value(GridXSide::BottomPlot).unwrap(), json!("bottom plot"));
assert_eq!(to_value(GridXSide::Top).unwrap(), json!("top"));
assert_eq!(to_value(GridXSide::TopPlot).unwrap(), json!("top plot"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_grid_y_side() {
assert_eq!(to_value(GridYSide::Left).unwrap(), json!("left"));
assert_eq!(to_value(GridYSide::LeftPlot).unwrap(), json!("left plot"));
assert_eq!(to_value(GridYSide::Right).unwrap(), json!("right"));
assert_eq!(to_value(GridYSide::RightPlot).unwrap(), json!("right plot"));
}
#[test]
fn test_serialize_grid_domain() {
let grid_domain = GridDomain::new().x(vec![0.0]).y(vec![1.0]);
let expected = json!({
"x": [0.0],
"y": [1.0]
});
assert_eq!(to_value(grid_domain).unwrap(), expected);
}
#[test]
fn test_serialize_layout_grid() {
let layout_grid = LayoutGrid::new()
.rows(224)
.row_order(RowOrder::BottomToTop)
.columns(501)
.sub_plots(vec!["subplots".to_string()])
.x_axes(vec!["xaxes".to_string()])
.y_axes(vec!["yaxes".to_string()])
.pattern(GridPattern::Coupled)
.x_gap(2.2)
.y_gap(4.4)
.domain(GridDomain::new())
.x_side(GridXSide::Top)
.y_side(GridYSide::Right);
let expected = json!({
"rows": 224,
"roworder": "bottom to top",
"columns": 501,
"subplots": ["subplots"],
"xaxes": ["xaxes"],
"yaxes": ["yaxes"],
"pattern": "coupled",
"xgap": 2.2,
"ygap": 4.4,
"domain": {},
"xside": "top",
"yside": "right",
});
assert_eq!(to_value(layout_grid).unwrap(), expected);
}
#[test]
fn test_serialize_uniform_text() {
let uniform_text = UniformText::new().mode(UniformTextMode::Hide).min_size(5);
let expected = json!({
"mode": "hide",
"minsize": 5
});
assert_eq!(to_value(uniform_text).unwrap(), expected);
}
#[test]
fn test_serialize_mode_bar() {
let mode_bar = ModeBar::new()
.orientation(Orientation::Horizontal)
.background_color("#FFF000")
.color("#000FFF")
.active_color("#AAABBB");
let expected = json!({
"orientation": "h",
"bgcolor": "#FFF000",
"color": "#000FFF",
"activecolor": "#AAABBB",
});
assert_eq!(to_value(mode_bar).unwrap(), expected);
}
#[test]
fn test_serialize_shape_type() {
assert_eq!(to_value(ShapeType::Circle).unwrap(), json!("circle"));
assert_eq!(to_value(ShapeType::Rect).unwrap(), json!("rect"));
assert_eq!(to_value(ShapeType::Path).unwrap(), json!("path"));
assert_eq!(to_value(ShapeType::Line).unwrap(), json!("line"));
}
#[test]
fn test_serialize_shape_layer() {
assert_eq!(to_value(ShapeLayer::Below).unwrap(), json!("below"));
assert_eq!(to_value(ShapeLayer::Above).unwrap(), json!("above"));
}
#[test]
fn test_serialize_shape_size_mode() {
assert_eq!(to_value(ShapeSizeMode::Scaled).unwrap(), json!("scaled"));
assert_eq!(to_value(ShapeSizeMode::Pixel).unwrap(), json!("pixel"));
}
#[test]
fn test_serialize_fill_rule() {
assert_eq!(to_value(FillRule::EvenOdd).unwrap(), json!("evenodd"));
assert_eq!(to_value(FillRule::NonZero).unwrap(), json!("nonzero"));
}
#[test]
fn test_serialize_shape_line() {
let shape_line = ShapeLine::new()
.color("#000FFF")
.width(100.)
.dash(DashType::LongDashDot);
let expected = json!({
"color": "#000FFF",
"width": 100.0,
"dash": "longdashdot",
});
assert_eq!(to_value(shape_line).unwrap(), expected);
}
#[test]
fn test_serialize_shape() {
let shape = Shape::new()
.visible(false)
.shape_type(ShapeType::Circle)
.layer(ShapeLayer::Above)
.x_ref("xref")
.x_size_mode(ShapeSizeMode::Pixel)
.x_anchor(5)
.x0(7)
.x1(8)
.y_ref("paper")
.y0(1)
.y1(2)
.y_anchor("yanchor")
.y_size_mode(ShapeSizeMode::Scaled)
.path("path")
.opacity(0.2)
.line(ShapeLine::new())
.fill_color("#FEFEFE")
.fill_rule(FillRule::NonZero)
.editable(true)
.name("name")
.template_item_name("templateitemname");
let expected = json!({
"visible": false,
"type": "circle",
"layer": "above",
"xref": "xref",
"xsizemode": "pixel",
"xanchor": 5,
"x0": 7,
"x1": 8,
"yref": "paper",
"y0": 1,
"y1": 2,
"yanchor": "yanchor",
"ysizemode": "scaled",
"path": "path",
"opacity": 0.2,
"line": {},
"fillcolor": "#FEFEFE",
"fillrule": "nonzero",
"editable": true,
"name": "name",
"templateitemname": "templateitemname"
});
assert_eq!(to_value(shape).unwrap(), expected)
}
#[test]
#[rustfmt::skip]
fn test_serialize_draw_direction() {
assert_eq!(to_value(DrawDirection::Ortho).unwrap(), json!("ortho"));
assert_eq!(to_value(DrawDirection::Horizontal).unwrap(), json!("horizontal"));
assert_eq!(to_value(DrawDirection::Vertical).unwrap(), json!("vertical"));
assert_eq!(to_value(DrawDirection::Diagonal).unwrap(), json!("diagonal"));
}
#[test]
fn test_serialize_new_shape() {
let new_shape = NewShape::new()
.line(ShapeLine::new())
.fill_color("#123ABC")
.fill_rule(FillRule::EvenOdd)
.opacity(0.02)
.layer(ShapeLayer::Below)
.draw_direction(DrawDirection::Ortho);
let expected = json!({
"line": {},
"fillcolor": "#123ABC",
"fillrule": "evenodd",
"opacity": 0.02,
"layer": "below",
"drawdirection": "ortho",
});
assert_eq!(to_value(new_shape).unwrap(), expected)
}
#[test]
fn test_serialize_active_shape() {
let active_shape = ActiveShape::new().fill_color("#123ABC").opacity(0.02);
let expected = json!({
"fillcolor": "#123ABC",
"opacity": 0.02,
});
assert_eq!(to_value(active_shape).unwrap(), expected);
}
#[test]
fn test_serialize_arrow_side() {
assert_eq!(to_value(ArrowSide::End).unwrap(), json!("end"));
assert_eq!(to_value(ArrowSide::Start).unwrap(), json!("start"));
assert_eq!(to_value(ArrowSide::StartEnd).unwrap(), json!("end+start"));
assert_eq!(to_value(ArrowSide::None).unwrap(), json!("none"));
}
#[test]
fn test_serialize_annotation() {
let annotation = Annotation::new()
.align(HAlign::Center)
.arrow_color("#464646")
.arrow_head(2)
.arrow_size(123.4)
.arrow_side(ArrowSide::End)
.arrow_width(111.1)
.ax("ax")
.ax_ref("axref")
.ay("ay")
.ay_ref("ayref")
.background_color("#123456")
.border_color("#456789")
.border_pad(500.)
.border_width(1000.)
.capture_events(false)
.click_to_show(ClickToShow::OnOff)
.font(Font::new())
.height(6.)
.hover_label(Label::new())
.hover_text("hovertext")
.name("name")
.opacity(0.01)
.show_arrow(false)
.stand_off(999.9)
.start_arrow_head(0)
.start_stand_off(8.8)
.start_arrow_size(456.7)
.template_item_name("templateitemname")
.text("text")
.text_angle(5.)
.valign(VAlign::Middle)
.visible(true)
.width(4.)
.x_ref("xref")
.x("x")
.x_anchor(Anchor::Auto)
.x_click("xclick")
.x_shift(4.0)
.y_ref("yref")
.y("y")
.y_anchor(Anchor::Bottom)
.y_click("yclick")
.y_shift(6.3);
let expected = json!({
"visible": true,
"text": "text",
"textangle": 5.0,
"font": {},
"width": 4.0,
"height": 6.0,
"opacity": 0.01,
"align": "center",
"valign": "middle",
"bgcolor": "#123456",
"bordercolor": "#456789",
"borderpad": 500.0,
"borderwidth": 1000.0,
"showarrow": false,
"arrowcolor": "#464646",
"arrowhead": 2,
"startarrowhead": 0,
"arrowside": "end",
"arrowsize": 123.4,
"startarrowsize": 456.7,
"arrowwidth": 111.1,
"standoff": 999.9,
"startstandoff": 8.8,
"ax": "ax",
"ay": "ay",
"x": "x",
"y": "y",
"axref": "axref",
"ayref": "ayref",
"xref": "xref",
"yref": "yref",
"xanchor": "auto",
"yanchor": "bottom",
"xshift": 4.0,
"yshift": 6.3,
"clicktoshow": "onoff",
"xclick": "xclick",
"yclick": "yclick",
"hovertext": "hovertext",
"hoverlabel": {},
"captureevents": false,
"name": "name",
"templateitemname": "templateitemname",
});
assert_eq!(to_value(annotation).unwrap(), expected);
}
#[test]
#[rustfmt::skip]
fn test_serialize_click_mode() {
assert_eq!(to_value(ClickMode::Event).unwrap(), json!("event"));
assert_eq!(to_value(ClickMode::Select).unwrap(), json!("select"));
assert_eq!(to_value(ClickMode::EventAndSelect).unwrap(), json!("event+select"));
assert_eq!(to_value(ClickMode::None).unwrap(), json!("none"));
}
#[test]
#[rustfmt::skip]
fn test_serialize_drag_mode() {
assert_eq!(to_value(DragMode::Zoom).unwrap(), json!("zoom"));
assert_eq!(to_value(DragMode::Pan).unwrap(), json!("pan"));
assert_eq!(to_value(DragMode::Select).unwrap(), json!("select"));
assert_eq!(to_value(DragMode::Lasso).unwrap(), json!("lasso"));
assert_eq!(to_value(DragMode::DrawClosedPath).unwrap(), json!("drawclosedpath"));
assert_eq!(to_value(DragMode::DrawOpenPath).unwrap(), json!("drawopenpath"));
assert_eq!(to_value(DragMode::DrawLine).unwrap(), json!("drawline"));
assert_eq!(to_value(DragMode::DrawRect).unwrap(), json!("drawrect"));
assert_eq!(to_value(DragMode::DrawCircle).unwrap(), json!("drawcircle"));
assert_eq!(to_value(DragMode::Orbit).unwrap(), json!("orbit"));
assert_eq!(to_value(DragMode::Turntable).unwrap(), json!("turntable"));
assert_eq!(to_value(DragMode::False).unwrap(), json!(false));
}
#[test]
#[rustfmt::skip]
fn test_serialize_mapbox_style() {
assert_eq!(to_value(MapboxStyle::CartoDarkMatter).unwrap(), json!("carto-darkmatter"));
assert_eq!(to_value(MapboxStyle::CartoPositron).unwrap(), json!("carto-positron"));
assert_eq!(to_value(MapboxStyle::OpenStreetMap).unwrap(), json!("open-street-map"));
assert_eq!(to_value(MapboxStyle::StamenTerrain).unwrap(), json!("stamen-terrain"));
assert_eq!(to_value(MapboxStyle::StamenToner).unwrap(), json!("stamen-toner"));
assert_eq!(to_value(MapboxStyle::StamenWatercolor).unwrap(), json!("stamen-watercolor"));
assert_eq!(to_value(MapboxStyle::WhiteBg).unwrap(), json!("white-bg"));
assert_eq!(to_value(MapboxStyle::Basic).unwrap(), json!("basic"));
assert_eq!(to_value(MapboxStyle::Streets).unwrap(), json!("streets"));
assert_eq!(to_value(MapboxStyle::Outdoors).unwrap(), json!("outdoors"));
assert_eq!(to_value(MapboxStyle::Light).unwrap(), json!("light"));
assert_eq!(to_value(MapboxStyle::Dark).unwrap(), json!("dark"));
assert_eq!(to_value(MapboxStyle::Satellite).unwrap(), json!("satellite"));
assert_eq!(to_value(MapboxStyle::SatelliteStreets).unwrap(), json!("satellite-streets"));
}
#[test]
fn test_serialize_select_direction() {
assert_eq!(to_value(SelectDirection::Horizontal).unwrap(), json!("h"));
assert_eq!(to_value(SelectDirection::Vertical).unwrap(), json!("v"));
assert_eq!(to_value(SelectDirection::Diagonal).unwrap(), json!("d"));
assert_eq!(to_value(SelectDirection::Any).unwrap(), json!("any"));
}
#[test]
fn test_serialize_layout_template() {
let layout_template = LayoutTemplate::new()
.title("Title".into())
.show_legend(false)
.legend(Legend::new())
.margin(Margin::new())
.auto_size(true)
.width(10)
.height(20)
.font(Font::new())
.uniform_text(UniformText::new())
.separators("_")
.paper_background_color("#FFFFFF")
.plot_background_color("#151515")
.color_scale(LayoutColorScale::new())
.colorway(vec!["#123123"])
.color_axis(ColorAxis::new())
.mode_bar(ModeBar::new())
.hover_mode(HoverMode::Closest)
.click_mode(ClickMode::EventAndSelect)
.drag_mode(DragMode::Turntable)
.select_direction(SelectDirection::Diagonal)
.hover_distance(321)
.spike_distance(12)
.hover_label(Label::new())
.grid(LayoutGrid::new())
.calendar(Calendar::Jalali)
.x_axis(Axis::new())
.x_axis2(Axis::new())
.x_axis3(Axis::new())
.x_axis4(Axis::new())
.x_axis5(Axis::new())
.x_axis6(Axis::new())
.x_axis7(Axis::new())
.x_axis8(Axis::new())
.y_axis(Axis::new())
.y_axis2(Axis::new())
.y_axis3(Axis::new())
.y_axis4(Axis::new())
.y_axis5(Axis::new())
.y_axis6(Axis::new())
.y_axis7(Axis::new())
.y_axis8(Axis::new())
.annotations(vec![Annotation::new()])
.shapes(vec![Shape::new()])
.new_shape(NewShape::new())
.active_shape(ActiveShape::new())
.box_mode(BoxMode::Group)
.box_gap(1.)
.box_group_gap(2.)
.bar_mode(BarMode::Overlay)
.bar_norm(BarNorm::Empty)
.bar_gap(3.)
.bar_group_gap(4.)
.violin_mode(ViolinMode::Overlay)
.violin_gap(5.)
.violin_group_gap(6.)
.waterfall_mode(WaterfallMode::Group)
.waterfall_gap(7.)
.waterfall_group_gap(8.)
.pie_colorway(vec!["#789789"])
.extend_pie_colors(true)
.sunburst_colorway(vec!["#654654"])
.extend_sunburst_colors(false);
let expected = json!({
"title": {"text": "Title"},
"showlegend": false,
"legend": {},
"margin": {},
"autosize": true,
"width": 10,
"height": 20,
"font": {},
"uniformtext": {},
"separators": "_",
"paper_bgcolor": "#FFFFFF",
"plot_bgcolor": "#151515",
"colorscale": {},
"colorway": ["#123123"],
"coloraxis": {},
"modebar": {},
"hovermode": "closest",
"clickmode": "event+select",
"dragmode": "turntable",
"selectdirection": "d",
"hoverdistance": 321,
"spikedistance": 12,
"hoverlabel": {},
"grid": {},
"calendar": "jalali",
"xaxis": {},
"xaxis2": {},
"xaxis3": {},
"xaxis4": {},
"xaxis5": {},
"xaxis6": {},
"xaxis7": {},
"xaxis8": {},
"yaxis": {},
"yaxis2": {},
"yaxis3": {},
"yaxis4": {},
"yaxis5": {},
"yaxis6": {},
"yaxis7": {},
"yaxis8": {},
"annotations": [{}],
"shapes": [{}],
"newshape": {},
"activeshape": {},
"boxmode": "group",
"boxgap": 1.0,
"boxgroupgap": 2.0,
"barmode": "overlay",
"barnorm": "",
"bargap": 3.0,
"bargroupgap": 4.0,
"violinmode": "overlay",
"violingap": 5.0,
"violingroupgap": 6.0,
"waterfallmode": "group",
"waterfallgap": 7.0,
"waterfallgroupgap": 8.0,
"piecolorway": ["#789789"],
"extendpiecolors": true,
"sunburstcolorway": ["#654654"],
"extendsunburstcolors": false,
});
assert_eq!(to_value(layout_template).unwrap(), expected);
}
#[test]
fn test_serialize_template() {
let template = Template::new().layout(LayoutTemplate::new());
let expected = json!({"layout": {}});
assert_eq!(to_value(template).unwrap(), expected);
}
#[test]
fn test_serialize_layout() {
let layout = Layout::new()
.title("Title".into())
.show_legend(false)
.legend(Legend::new())
.margin(Margin::new())
.auto_size(true)
.width(10)
.height(20)
.font(Font::new())
.uniform_text(UniformText::new())
.separators("_")
.paper_background_color("#FFFFFF")
.plot_background_color("#151515")
.color_scale(LayoutColorScale::new())
.colorway(vec!["#123123"])
.color_axis(ColorAxis::new())
.mode_bar(ModeBar::new())
.hover_mode(HoverMode::Closest)
.click_mode(ClickMode::EventAndSelect)
.drag_mode(DragMode::Turntable)
.select_direction(SelectDirection::Diagonal)
.hover_distance(321)
.spike_distance(12)
.hover_label(Label::new())
.template(Template::new())
.grid(LayoutGrid::new())
.calendar(Calendar::Jalali)
.x_axis(Axis::new())
.x_axis2(Axis::new())
.x_axis3(Axis::new())
.x_axis4(Axis::new())
.x_axis5(Axis::new())
.x_axis6(Axis::new())
.x_axis7(Axis::new())
.x_axis8(Axis::new())
.y_axis(Axis::new())
.y_axis2(Axis::new())
.y_axis3(Axis::new())
.y_axis4(Axis::new())
.y_axis5(Axis::new())
.y_axis6(Axis::new())
.y_axis7(Axis::new())
.y_axis8(Axis::new())
.annotations(vec![Annotation::new()])
.shapes(vec![Shape::new()])
.new_shape(NewShape::new())
.active_shape(ActiveShape::new())
.box_mode(BoxMode::Group)
.box_gap(1.)
.box_group_gap(2.)
.bar_mode(BarMode::Overlay)
.bar_norm(BarNorm::Empty)
.bar_gap(3.)
.bar_group_gap(4.)
.violin_mode(ViolinMode::Overlay)
.violin_gap(5.)
.violin_group_gap(6.)
.waterfall_mode(WaterfallMode::Group)
.waterfall_gap(7.)
.waterfall_group_gap(8.)
.pie_colorway(vec!["#789789"])
.extend_pie_colors(true)
.sunburst_colorway(vec!["#654654"])
.extend_sunburst_colors(false)
.z_axis(Axis::new());
let expected = json!({
"title": {"text": "Title"},
"showlegend": false,
"legend": {},
"margin": {},
"autosize": true,
"width": 10,
"height": 20,
"font": {},
"uniformtext": {},
"separators": "_",
"paper_bgcolor": "#FFFFFF",
"plot_bgcolor": "#151515",
"colorscale": {},
"colorway": ["#123123"],
"coloraxis": {},
"modebar": {},
"hovermode": "closest",
"clickmode": "event+select",
"dragmode": "turntable",
"selectdirection": "d",
"hoverdistance": 321,
"spikedistance": 12,
"hoverlabel": {},
"template": {},
"grid": {},
"calendar": "jalali",
"xaxis": {},
"xaxis2": {},
"xaxis3": {},
"xaxis4": {},
"xaxis5": {},
"xaxis6": {},
"xaxis7": {},
"xaxis8": {},
"yaxis": {},
"yaxis2": {},
"yaxis3": {},
"yaxis4": {},
"yaxis5": {},
"yaxis6": {},
"yaxis7": {},
"yaxis8": {},
"annotations": [{}],
"shapes": [{}],
"newshape": {},
"activeshape": {},
"boxmode": "group",
"boxgap": 1.0,
"boxgroupgap": 2.0,
"barmode": "overlay",
"barnorm": "",
"bargap": 3.0,
"bargroupgap": 4.0,
"violinmode": "overlay",
"violingap": 5.0,
"violingroupgap": 6.0,
"waterfallmode": "group",
"waterfallgap": 7.0,
"waterfallgroupgap": 8.0,
"piecolorway": ["#789789"],
"extendpiecolors": true,
"sunburstcolorway": ["#654654"],
"extendsunburstcolors": false,
"zaxis": {},
});
assert_eq!(to_value(layout).unwrap(), expected);
}
}