use serde::{Deserialize, Serialize};
use crate::node::{Common, IntoNode, Node};
use crate::style::Style;
use crate::text::{Alignment, Line, Span};
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum Marker {
Dot,
#[default]
Braille,
Block,
Bar,
HalfBlock,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum GraphType {
#[default]
Scatter,
Line,
Bar,
}
#[derive(Clone, Copy, Debug, Default, PartialEq, Eq, Hash, Serialize, Deserialize)]
pub enum LegendPosition {
#[default]
Top,
TopRight,
TopLeft,
Left,
Right,
Bottom,
BottomRight,
BottomLeft,
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Axis {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub title: Option<Line>,
#[serde(default)]
pub bounds: [f64; 2],
#[serde(default, skip_serializing_if = "Vec::is_empty")]
pub labels: Vec<Span>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels_alignment: Option<Alignment>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub style: Option<Style>,
}
impl Axis {
#[must_use]
pub fn bounds(min: f64, max: f64) -> Self {
Self {
title: None,
bounds: [min, max],
labels: Vec::new(),
labels_alignment: None,
style: None,
}
}
#[must_use]
pub fn title(mut self, title: impl Into<Line>) -> Self {
self.title = Some(title.into());
self
}
#[must_use]
pub fn labels(mut self, labels: impl IntoIterator<Item = Span>) -> Self {
self.labels = labels.into_iter().collect();
self
}
#[must_use]
pub fn labels_alignment(mut self, alignment: Alignment) -> Self {
self.labels_alignment = Some(alignment);
self
}
#[must_use]
pub fn style(mut self, style: Style) -> Self {
self.style = Some(style);
self
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Dataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub name: Option<Line>,
pub data: Vec<[f64; 2]>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub marker: Option<Marker>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub graph_type: Option<GraphType>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub style: Option<Style>,
}
impl Dataset {
#[must_use]
pub fn new(data: impl IntoIterator<Item = [f64; 2]>) -> Self {
Self {
data: data.into_iter().collect(),
..Self::default()
}
}
#[must_use]
pub fn name(mut self, name: impl Into<Line>) -> Self {
self.name = Some(name.into());
self
}
#[must_use]
pub fn marker(mut self, marker: Marker) -> Self {
self.marker = Some(marker);
self
}
#[must_use]
pub fn graph_type(mut self, graph_type: GraphType) -> Self {
self.graph_type = Some(graph_type);
self
}
#[must_use]
pub fn style(mut self, style: Style) -> Self {
self.style = Some(style);
self
}
}
#[derive(Clone, Debug, Default, PartialEq, Serialize, Deserialize)]
#[serde(rename_all = "camelCase")]
pub struct Chart {
#[serde(flatten)]
pub common: Common,
pub datasets: Vec<Dataset>,
#[serde(default)]
pub x_axis: Axis,
#[serde(default)]
pub y_axis: Axis,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub legend_position: Option<LegendPosition>,
}
impl Chart {
#[must_use]
pub fn new(datasets: impl IntoIterator<Item = Dataset>) -> Self {
Self {
datasets: datasets.into_iter().collect(),
..Self::default()
}
}
#[must_use]
pub fn x_axis(mut self, axis: Axis) -> Self {
self.x_axis = axis;
self
}
#[must_use]
pub fn y_axis(mut self, axis: Axis) -> Self {
self.y_axis = axis;
self
}
#[must_use]
pub fn legend_position(mut self, position: LegendPosition) -> Self {
self.legend_position = Some(position);
self
}
#[must_use]
pub fn hide_legend(mut self) -> Self {
self.legend_position = None;
self
}
}
impl IntoNode for Chart {}
impl From<Chart> for Node {
fn from(c: Chart) -> Self {
Node::Chart(c)
}
}