1use plotly_derive::FieldSetter;
4use serde::Serialize;
5
6use crate::{
7 common::{
8 Calendar, ConstrainText, Dim, ErrorData, Font, HoverInfo, Label, LegendGroupTitle, Marker,
9 Orientation, PlotType, TextAnchor, TextPosition, Visible,
10 },
11 Trace,
12};
13
14#[serde_with::skip_serializing_none]
37#[derive(Serialize, Debug, Clone, FieldSetter)]
38#[field_setter(box_self, kind = "trace")]
39pub struct Bar<X, Y>
40where
41 X: Serialize + Clone,
42 Y: Serialize + Clone,
43{
44 #[field_setter(default = "PlotType::Bar")]
45 r#type: PlotType,
46 x: Option<Vec<X>>,
47 y: Option<Vec<Y>>,
48 name: Option<String>,
49 visible: Option<Visible>,
50 #[serde(rename = "showlegend")]
51 show_legend: Option<bool>,
52 #[serde(rename = "legendgroup")]
53 legend_group: Option<String>,
54 #[serde(rename = "legendgrouptitle")]
55 legend_group_title: Option<LegendGroupTitle>,
56 opacity: Option<f64>,
57 ids: Option<Vec<String>>,
58 width: Option<usize>,
59 offset: Option<Dim<usize>>,
60 text: Option<Dim<String>>,
61 #[serde(rename = "textposition")]
62 text_position: Option<Dim<TextPosition>>,
63 #[serde(rename = "texttemplate")]
64 text_template: Option<Dim<String>>,
65 #[serde(rename = "hovertext")]
66 hover_text: Option<Dim<String>>,
67 #[serde(rename = "hoverinfo")]
68 hover_info: Option<HoverInfo>,
69 #[serde(rename = "hovertemplate")]
70 hover_template: Option<Dim<String>>,
71 #[serde(rename = "xaxis")]
72 x_axis: Option<String>,
73 #[serde(rename = "yaxis")]
74 y_axis: Option<String>,
75 orientation: Option<Orientation>,
76 #[serde(rename = "alignmentgroup")]
77 alignment_group: Option<String>,
78 #[serde(rename = "offsetgroup")]
79 offset_group: Option<String>,
80 marker: Option<Marker>,
81 #[serde(rename = "textangle")]
82 text_angle: Option<f64>,
83 #[serde(rename = "textfont")]
84 text_font: Option<Font>,
85 error_x: Option<ErrorData>,
86 error_y: Option<ErrorData>,
87 #[serde(rename = "cliponaxis")]
88 clip_on_axis: Option<bool>,
89 #[serde(rename = "constraintext")]
90 constrain_text: Option<ConstrainText>,
91 #[serde(rename = "hoverlabel")]
92 hover_label: Option<Label>,
93 #[serde(rename = "insidetextanchor")]
94 inside_text_anchor: Option<TextAnchor>,
95 #[serde(rename = "insidetextfont")]
96 inside_text_font: Option<Font>,
97 #[serde(rename = "outsidetextfont")]
98 outside_text_font: Option<Font>,
99 #[serde(rename = "xcalendar")]
100 x_calendar: Option<Calendar>,
101 #[serde(rename = "ycalendar")]
102 y_calendar: Option<Calendar>,
103}
104
105impl<X, Y> Bar<X, Y>
106where
107 X: Serialize + Clone,
108 Y: Serialize + Clone,
109{
110 pub fn new(x: Vec<X>, y: Vec<Y>) -> Box<Self> {
111 Box::new(Bar {
112 x: Some(x),
113 y: Some(y),
114 ..Default::default()
115 })
116 }
117}
118
119impl<X, Y> Trace for Bar<X, Y>
120where
121 X: Serialize + Clone,
122 Y: Serialize + Clone,
123{
124 fn to_json(&self) -> String {
125 serde_json::to_string(self).unwrap()
126 }
127}
128
129#[cfg(test)]
130mod tests {
131 use serde_json::{json, to_value};
132
133 use super::*;
134 use crate::common::ErrorType;
135
136 #[test]
137 fn test_default_bar() {
138 let trace: Bar<i32, i32> = Bar::default();
139 let expected = json!({"type": "bar"}).to_string();
140
141 assert_eq!(trace.to_json(), expected);
142 }
143
144 #[test]
145 fn test_serialize_bar() {
146 let bar = Bar::new(vec![1, 2], vec![3, 4])
147 .alignment_group("alignment_group")
148 .clip_on_axis(true)
149 .constrain_text(ConstrainText::Both)
150 .error_x(ErrorData::new(ErrorType::Constant))
151 .error_y(ErrorData::new(ErrorType::Percent))
152 .hover_info(HoverInfo::All)
153 .hover_label(Label::new())
154 .hover_template("tmpl")
155 .hover_template_array(vec!["tmpl1", "tmpl2"])
156 .hover_text("hover_text")
157 .hover_text_array(vec!["hover_text"])
158 .ids(vec!["1"])
159 .inside_text_anchor(TextAnchor::End)
160 .inside_text_font(Font::new())
161 .legend_group("legend-group")
162 .legend_group_title(LegendGroupTitle::new("legend-group-title"))
163 .marker(Marker::new())
164 .name("Bar")
165 .offset(5)
166 .offset_array(vec![5, 5])
167 .offset_group("offset_group")
168 .opacity(0.5)
169 .orientation(Orientation::Vertical)
170 .outside_text_font(Font::new())
171 .show_legend(false)
172 .text("text")
173 .text_angle(0.05)
174 .text_array(vec!["text"])
175 .text_font(Font::new())
176 .text_position(TextPosition::None)
177 .text_position_array(vec![TextPosition::None])
178 .text_template("text_template")
179 .text_template_array(vec!["text_template"])
180 .visible(Visible::LegendOnly)
181 .width(999)
182 .x_axis("xaxis")
183 .x_calendar(Calendar::Nanakshahi)
184 .y_axis("yaxis")
185 .y_calendar(Calendar::Ummalqura);
186
187 let expected = json!({
188 "type": "bar",
189 "hoverinfo": "all",
190 "hovertemplate": ["tmpl1", "tmpl2"],
191 "x": [1, 2],
192 "y": [3, 4],
193 "name": "Bar",
194 "visible": "legendonly",
195 "showlegend": false,
196 "legendgroup": "legend-group",
197 "legendgrouptitle": {"text": "legend-group-title"},
198 "opacity": 0.5,
199 "ids": ["1"],
200 "width": 999,
201 "offset": [5, 5],
202 "text": ["text"],
203 "textposition": ["none"],
204 "texttemplate": ["text_template"],
205 "hovertext": ["hover_text"],
206 "xaxis": "xaxis",
207 "yaxis": "yaxis",
208 "orientation": "v",
209 "alignmentgroup": "alignment_group",
210 "offsetgroup": "offset_group",
211 "marker": {},
212 "textangle": 0.05,
213 "textfont": {},
214 "error_x": {"type": "constant"},
215 "error_y": {"type": "percent"},
216 "cliponaxis": true,
217 "constraintext": "both",
218 "hoverlabel": {},
219 "insidetextanchor": "end",
220 "insidetextfont": {},
221 "outsidetextfont": {},
222 "xcalendar": "nanakshahi",
223 "ycalendar": "ummalqura",
224 });
225
226 assert_eq!(to_value(bar).unwrap(), expected);
227 }
228}