1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
//! Bar trace

use plotly_derive::FieldSetter;
use serde::Serialize;

use crate::{
    common::{
        Calendar, ConstrainText, Dim, ErrorData, Font, HoverInfo, Label, Marker, Orientation,
        PlotType, TextAnchor, TextPosition, Visible,
    },
    Trace,
};

/// Construct a bar trace.
///
/// # Examples
///
/// ```
/// use plotly::Bar;
///
/// let x = vec![0, 1, 2, 3, 4, 5];
/// let y = vec![0, 2, 4, 6, 8, 10];
///
/// let trace = Bar::new(x, y).show_legend(true).opacity(0.5);
///
/// let expected = serde_json::json!({
///     "type": "bar",
///     "x": [0, 1, 2, 3, 4, 5],
///     "y": [0, 2, 4, 6, 8, 10],
///     "showlegend": true,
///     "opacity": 0.5
/// });
///
/// assert_eq!(serde_json::to_value(trace).unwrap(), expected);
/// ```
#[serde_with::skip_serializing_none]
#[derive(Serialize, Debug, Clone, FieldSetter)]
#[field_setter(box_self, kind = "trace")]
pub struct Bar<X, Y>
where
    X: Serialize + Clone,
    Y: Serialize + Clone,
{
    #[field_setter(default = "PlotType::Bar")]
    r#type: PlotType,
    x: Option<Vec<X>>,
    y: Option<Vec<Y>>,
    name: Option<String>,
    visible: Option<Visible>,
    #[serde(rename = "showlegend")]
    show_legend: Option<bool>,
    #[serde(rename = "legendgroup")]
    legend_group: Option<String>,
    opacity: Option<f64>,
    ids: Option<Vec<String>>,
    width: Option<usize>,
    offset: Option<Dim<usize>>,
    text: Option<Dim<String>>,
    #[serde(rename = "textposition")]
    text_position: Option<Dim<TextPosition>>,
    #[serde(rename = "texttemplate")]
    text_template: Option<Dim<String>>,
    #[serde(rename = "hovertext")]
    hover_text: Option<Dim<String>>,
    #[serde(rename = "hoverinfo")]
    hover_info: Option<HoverInfo>,
    #[serde(rename = "hovertemplate")]
    hover_template: Option<Dim<String>>,
    #[serde(rename = "xaxis")]
    x_axis: Option<String>,
    #[serde(rename = "yaxis")]
    y_axis: Option<String>,
    orientation: Option<Orientation>,
    #[serde(rename = "alignmentgroup")]
    alignment_group: Option<String>,
    #[serde(rename = "offsetgroup")]
    offset_group: Option<String>,
    marker: Option<Marker>,
    #[serde(rename = "textangle")]
    text_angle: Option<f64>,
    #[serde(rename = "textfont")]
    text_font: Option<Font>,
    error_x: Option<ErrorData>,
    error_y: Option<ErrorData>,
    #[serde(rename = "cliponaxis")]
    clip_on_axis: Option<bool>,
    #[serde(rename = "constraintext")]
    constrain_text: Option<ConstrainText>,
    #[serde(rename = "hoverlabel")]
    hover_label: Option<Label>,
    #[serde(rename = "insidetextanchor")]
    inside_text_anchor: Option<TextAnchor>,
    #[serde(rename = "insidetextfont")]
    inside_text_font: Option<Font>,
    #[serde(rename = "outsidetextfont")]
    outside_text_font: Option<Font>,
    #[serde(rename = "xcalendar")]
    x_calendar: Option<Calendar>,
    #[serde(rename = "ycalendar")]
    y_calendar: Option<Calendar>,
}

impl<X, Y> Bar<X, Y>
where
    X: Serialize + Clone,
    Y: Serialize + Clone,
{
    pub fn new(x: Vec<X>, y: Vec<Y>) -> Box<Self> {
        Box::new(Bar {
            x: Some(x),
            y: Some(y),
            ..Default::default()
        })
    }
}

impl<X, Y> Trace for Bar<X, Y>
where
    X: Serialize + Clone,
    Y: Serialize + Clone,
{
    fn to_json(&self) -> String {
        serde_json::to_string(self).unwrap()
    }
}

#[cfg(test)]
mod tests {
    use serde_json::{json, to_value};

    use super::*;
    use crate::common::ErrorType;

    #[test]
    fn test_default_bar() {
        let trace: Bar<i32, i32> = Bar::default();
        let expected = json!({"type": "bar"}).to_string();

        assert_eq!(trace.to_json(), expected);
    }

    #[test]
    fn test_serialize_bar() {
        let bar = Bar::new(vec![1, 2], vec![3, 4])
            .alignment_group("alignment_group")
            .clip_on_axis(true)
            .constrain_text(ConstrainText::Both)
            .error_x(ErrorData::new(ErrorType::Constant))
            .error_y(ErrorData::new(ErrorType::Percent))
            .hover_info(HoverInfo::All)
            .hover_label(Label::new())
            .hover_template("tmpl")
            .hover_template_array(vec!["tmpl1", "tmpl2"])
            .hover_text("hover_text")
            .hover_text_array(vec!["hover_text"])
            .ids(vec!["1"])
            .inside_text_anchor(TextAnchor::End)
            .inside_text_font(Font::new())
            .legend_group("legend-group")
            .marker(Marker::new())
            .name("Bar")
            .offset(5)
            .offset_array(vec![5, 5])
            .offset_group("offset_group")
            .opacity(0.5)
            .orientation(Orientation::Vertical)
            .outside_text_font(Font::new())
            .show_legend(false)
            .text("text")
            .text_angle(0.05)
            .text_array(vec!["text"])
            .text_font(Font::new())
            .text_position(TextPosition::None)
            .text_position_array(vec![TextPosition::None])
            .text_template("text_template")
            .text_template_array(vec!["text_template"])
            .visible(Visible::LegendOnly)
            .width(999)
            .x_axis("xaxis")
            .x_calendar(Calendar::Nanakshahi)
            .y_axis("yaxis")
            .y_calendar(Calendar::Ummalqura);

        let expected = json!({
            "type": "bar",
            "hoverinfo": "all",
            "hovertemplate": ["tmpl1", "tmpl2"],
            "x": [1, 2],
            "y": [3, 4],
            "name": "Bar",
            "visible": "legendonly",
            "showlegend": false,
            "legendgroup": "legend-group",
            "opacity": 0.5,
            "ids": ["1"],
            "width": 999,
            "offset": [5, 5],
            "text": ["text"],
            "textposition": ["none"],
            "texttemplate": ["text_template"],
            "hovertext": ["hover_text"],
            "xaxis": "xaxis",
            "yaxis": "yaxis",
            "orientation": "v",
            "alignmentgroup": "alignment_group",
            "offsetgroup": "offset_group",
            "marker": {},
            "textangle": 0.05,
            "textfont": {},
            "error_x": {"type": "constant"},
            "error_y": {"type": "percent"},
            "cliponaxis": true,
            "constraintext": "both",
            "hoverlabel": {},
            "insidetextanchor": "end",
            "insidetextfont": {},
            "outsidetextfont": {},
            "xcalendar": "nanakshahi",
            "ycalendar": "ummalqura",
        });

        assert_eq!(to_value(bar).unwrap(), expected);
    }
}