use super::common::{
AxisOptions, ColorString, DataLabelsPlugin, DecimationPlugin, LegendPlugin, ScalarOrArray,
ThemeOptions, TitlePlugin,
};
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(tag = "type", rename_all = "lowercase", deny_unknown_fields)]
pub enum ChartJsSpec {
Bar(BarSpec),
Line(LineSpec),
Pie(PieSpec),
Doughnut(PieSpec),
Scatter(ScatterSpec),
Bubble(BubbleSpec),
Radar(RadarSpec),
Matrix(MatrixSpec),
Treemap(TreemapSpec),
#[serde(alias = "progressBar")]
Progress(ProgressSpec),
Boxplot(BoxplotSpec),
Sparkline(SparklineSpec),
Gauge(GaugeSpec),
#[serde(rename = "polarArea")]
PolarArea(PieSpec),
#[serde(rename = "radialGauge")]
RadialGauge(RadialGaugeSpec),
#[serde(rename = "outlabeledPie")]
OutlabeledPie(OutlabeledPieSpec),
#[serde(rename = "outlabeledDoughnut")]
OutlabeledDoughnut(OutlabeledPieSpec),
#[serde(rename = "wordCloud")]
WordCloud(WordCloudSpec),
Sankey(SankeySpec),
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BarSpec {
pub data: BarData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<BarOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BarData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
pub datasets: Vec<BarDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BarDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(rename = "type", skip_serializing_if = "Option::is_none")]
pub dataset_type: Option<BarOrLine>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tension: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fill: Option<FillSpec>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum BarOrLine {
Bar,
Line,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BarOptions {
#[serde(rename = "indexAxis", skip_serializing_if = "Option::is_none")]
pub index_axis: Option<IndexAxis>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<BarPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scales: Option<BarScales>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum IndexAxis {
X,
Y,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct BarPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub legend: Option<LegendPlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub datalabels: Option<DataLabelsPlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decimation: Option<DecimationPlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct BarScales {
#[serde(skip_serializing_if = "Option::is_none")]
pub x: Option<AxisOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub y: Option<AxisOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct LineSpec {
pub data: LineData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<LineOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct LineData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
pub datasets: Vec<LineDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct LineDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tension: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fill: Option<FillSpec>,
#[serde(skip_serializing_if = "Option::is_none")]
pub point_radius: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum FillSpec {
Bool(bool),
Mode(String),
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct LineOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<CommonPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scales: Option<BarScales>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct CommonPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub legend: Option<LegendPlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub datalabels: Option<DataLabelsPlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decimation: Option<DecimationPlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct GaugePlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct ProgressPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub datalabels: Option<DataLabelsPlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PieSpec {
pub data: PieData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<PieOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PieData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
pub datasets: Vec<PieDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct PieDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct PieOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<CommonPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ScatterSpec {
pub data: ScatterData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<XYOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ScatterData {
pub datasets: Vec<ScatterDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ScatterDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<XYPoint>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub point_radius: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct XYPoint {
pub x: f64,
pub y: f64,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct XYOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<CommonPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scales: Option<BarScales>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BubbleSpec {
pub data: BubbleData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<XYOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BubbleData {
pub datasets: Vec<BubbleDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BubbleDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<XYRPoint>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct XYRPoint {
pub x: f64,
pub y: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub r: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RadarSpec {
pub data: RadarData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<RadarOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RadarData {
pub labels: Vec<String>,
pub datasets: Vec<RadarDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RadarDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub tension: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fill: Option<FillSpec>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RadarOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<CommonPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MatrixSpec {
pub data: MatrixData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<MatrixOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MatrixData {
pub datasets: Vec<MatrixDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct MatrixDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<MatrixPoint>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MatrixPoint {
pub x: String,
pub y: String,
pub v: f64,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct MatrixOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<MatrixPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct MatrixPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub legend: Option<LegendPlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub decimation: Option<DecimationPlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TreemapSpec {
pub data: TreemapData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<TreemapOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TreemapData {
pub datasets: Vec<TreemapDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct TreemapDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub tree: TreemapTree,
#[serde(skip_serializing_if = "Option::is_none")]
pub key: Option<String>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub groups: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum TreemapTree {
Numbers(Vec<f64>),
Objects(Vec<serde_json::Map<String, serde_json::Value>>),
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct TreemapOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<TreemapPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct TreemapPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ProgressSpec {
pub data: ProgressData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<ProgressOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ProgressData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
pub datasets: Vec<ProgressDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ProgressDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct ProgressOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<ProgressPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BoxplotSpec {
pub data: BoxplotData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<BoxplotOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BoxplotData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
pub datasets: Vec<BoxplotDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct BoxplotDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<Vec<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct BoxplotOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<CommonPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scales: Option<BarScales>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SparklineSpec {
pub data: SparklineData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<SparklineOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SparklineData {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
pub datasets: Vec<SparklineDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SparklineDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ScalarOrArray<ColorString>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_width: Option<f64>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub tension: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub fill: Option<FillSpec>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct SparklinePlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub decimation: Option<DecimationPlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SparklineOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<SparklinePlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub scales: Option<BarScales>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct GaugeSpec {
pub data: GaugeData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<GaugeOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct GaugeData {
pub datasets: Vec<GaugeDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GaugeDataset {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub min_value: Option<f64>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct GaugeOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub needle: Option<NeedleOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub value_label: Option<ValueLabelOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<GaugePlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct NeedleOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ColorString>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct ValueLabelOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub display: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ColorString>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RadialGaugeSpec {
pub data: RadialGaugeData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<RadialGaugeOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct RadialGaugeData {
pub datasets: Vec<RadialGaugeDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RadialGaugeDataset {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ScalarOrArray<ColorString>>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct RadialGaugeOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub domain: Option<Vec<f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub track_color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub center_percentage: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rounded_corners: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub center_area: Option<CenterAreaOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<GaugePlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct CenterAreaOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub display_text: Option<bool>,
#[serde(skip_serializing_if = "Option::is_none")]
pub font_size: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct OutlabeledPieSpec {
pub data: PieData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<OutlabeledPieOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct OutlabeledPieOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<OutlabeledPiePlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct OutlabeledPiePlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub legend: Option<LegendPlugin>,
#[serde(skip_serializing_if = "Option::is_none")]
pub outlabels: Option<OutlabelsPlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct OutlabelsPlugin {
#[serde(skip_serializing_if = "Option::is_none")]
pub text: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub background_color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub stretch: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct WordCloudSpec {
pub data: WordCloudData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<WordCloudOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct WordCloudData {
pub labels: Vec<String>,
pub datasets: Vec<WordCloudDataset>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WordCloudDataset {
#[serde(skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ScalarOrArray<ColorString>>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct WordCloudOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub elements: Option<WordCloudElements>,
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<WordCloudPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct WordCloudElements {
#[serde(skip_serializing_if = "Option::is_none")]
pub word: Option<WordElementOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct WordElementOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub min_rotation: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub max_rotation: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub rotation_steps: Option<u32>,
#[serde(skip_serializing_if = "Option::is_none")]
pub padding: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema, Default)]
#[serde(deny_unknown_fields)]
pub struct WordCloudPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SankeySpec {
pub data: SankeyData,
#[serde(skip_serializing_if = "Option::is_none")]
pub options: Option<SankeyOptions>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 1.0, max = 32768.0))]
pub height: Option<f64>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SankeyOptions {
#[serde(skip_serializing_if = "Option::is_none")]
pub plugins: Option<SankeyPlugins>,
#[serde(skip_serializing_if = "Option::is_none")]
pub theme: Option<ThemeOptions>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SankeyPlugins {
#[serde(skip_serializing_if = "Option::is_none")]
pub title: Option<TitlePlugin>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(deny_unknown_fields)]
pub struct SankeyData {
#[schemars(length(min = 1, max = 1))]
pub datasets: Vec<SankeyDataset>,
#[serde(default, skip_serializing_if = "Option::is_none")]
pub labels: Option<Vec<String>>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SankeyParsing {
#[serde(skip_serializing_if = "Option::is_none")]
pub from: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub to: Option<String>,
#[serde(skip_serializing_if = "Option::is_none")]
pub flow: Option<String>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(untagged)]
pub enum SankeyParsingSpec {
Keys(SankeyParsing),
Disabled(SankeyParsingDisabled),
}
#[derive(Clone, Copy, Serialize, Deserialize)]
#[serde(try_from = "bool", into = "bool")]
pub struct SankeyParsingDisabled;
impl TryFrom<bool> for SankeyParsingDisabled {
type Error = &'static str;
fn try_from(b: bool) -> Result<Self, Self::Error> {
if b {
Err("dataset.parsing accepts an object or `false`; `true` is not supported")
} else {
Ok(SankeyParsingDisabled)
}
}
}
impl From<SankeyParsingDisabled> for bool {
fn from(_: SankeyParsingDisabled) -> Self {
false
}
}
impl JsonSchema for SankeyParsingDisabled {
fn schema_name() -> std::borrow::Cow<'static, str> {
"SankeyParsingDisabled".into()
}
fn json_schema(_: &mut schemars::SchemaGenerator) -> schemars::Schema {
schemars::json_schema!({
"type": "boolean",
"const": false,
"description": "Explicit `false` disables parsing key remapping (equivalent to omitting `parsing`)."
})
}
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SankeyDataset {
#[serde(default, skip_serializing_if = "Option::is_none")]
pub label: Option<String>,
pub data: Vec<SankeyFlow>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color_from: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color_to: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color_mode: Option<SankeyColorModeOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hover_color_from: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub hover_color_to: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub alpha: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub border_color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 0.0, max = 32768.0))]
pub border_width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 0.0, max = 32768.0))]
pub node_width: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
#[schemars(range(min = 0.0, max = 32768.0))]
pub node_padding: Option<f64>,
#[serde(skip_serializing_if = "Option::is_none")]
pub mode_x: Option<SankeyModeXOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub size: Option<SankeySizeOption>,
#[serde(skip_serializing_if = "Option::is_none")]
pub labels: Option<std::collections::HashMap<String, String>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub priority: Option<std::collections::HashMap<String, f64>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub column: Option<std::collections::HashMap<String, u32>>,
#[serde(skip_serializing_if = "Option::is_none")]
pub parsing: Option<SankeyParsingSpec>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "camelCase", deny_unknown_fields)]
pub struct SankeyFlow {
pub from: String,
pub to: String,
#[schemars(range(min = 0.0))]
pub flow: f64,
#[serde(skip_serializing_if = "Option::is_none")]
pub color: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color_from: Option<ColorString>,
#[serde(skip_serializing_if = "Option::is_none")]
pub color_to: Option<ColorString>,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum SankeyColorModeOption {
From,
To,
Gradient,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum SankeyModeXOption {
Edge,
Even,
}
#[derive(Serialize, Deserialize, JsonSchema)]
#[serde(rename_all = "lowercase")]
pub enum SankeySizeOption {
Min,
Max,
}
#[cfg(test)]
mod tests {
use super::ChartJsSpec;
#[test]
fn sankey_datasets_constrained_to_one_in_schema() {
let schema = schemars::schema_for!(ChartJsSpec);
let json = serde_json::to_string(&schema).unwrap();
assert!(
json.contains("\"minItems\":1"),
"sankey datasets に minItems:1 が必要"
);
assert!(
json.contains("\"maxItems\":1"),
"sankey datasets に maxItems:1 が必要"
);
assert!(
json.contains("\"minimum\":0"),
"sankey の寸法/flow に minimum:0 が必要"
);
assert!(
json.contains("\"maximum\":32768"),
"sankey の寸法に maximum:32768 が必要"
);
}
}