pub mod builder;
pub mod rendering;
pub mod specification;
pub mod types;
pub mod validation;
pub use builder::*;
pub use rendering::*;
pub use specification::*;
pub use types::*;
pub use validation::*;
use serde::{Deserialize, Serialize};
#[derive(Debug, Clone, PartialEq, Serialize, Deserialize)]
pub struct ChartSpec {
pub data: DataReference,
pub mark: MarkType,
pub encoding: Encoding,
pub transform: Vec<Transform>,
pub selection: Vec<Selection>,
pub intelligence: Option<Intelligence>,
pub config: ChartConfig,
}
impl ChartSpec {
pub fn new() -> Self {
ChartSpecBuilder::default()
.data(DataReference {
source: "default".to_string(),
format: DataFormat::Inline,
schema: None,
})
.mark(MarkType::Point { size: None, shape: None, opacity: None })
.encoding(Encoding {
x: Some(EncodingDef {
field: "x".to_string(),
data_type: DataType::Number,
scale: None,
axis: None,
legend: None,
bin: None,
aggregate: None,
sort: None,
}),
y: Some(EncodingDef {
field: "y".to_string(),
data_type: DataType::Number,
scale: None,
axis: None,
legend: None,
bin: None,
aggregate: None,
sort: None,
}),
color: None,
size: None,
shape: None,
opacity: None,
text: None,
tooltip: None,
detail: None,
order: None,
row: None,
column: None,
})
.build()
.expect("Failed to build default chart spec")
}
pub fn validate(&self) -> Result<(), ValidationError> {
self.data.validate()?;
self.encoding.validate_for_mark(&self.mark)?;
for transform in &self.transform {
transform.validate()?;
}
for selection in &self.selection {
selection.validate()?;
}
if let Some(intelligence) = &self.intelligence {
intelligence.validate()?;
}
Ok(())
}
pub fn optimize(self) -> Self {
let optimized_encoding = self.encoding.optimize();
let optimized_transforms = self.transform.into_iter().map(|t| t.optimize()).collect();
Self {
encoding: optimized_encoding,
transform: optimized_transforms,
..self
}
}
pub fn complexity(&self) -> f64 {
let base_complexity = match self.mark {
MarkType::Point { .. } => 1.0,
MarkType::Line { .. } => 2.0,
MarkType::Bar { .. } => 1.5,
MarkType::Area { .. } => 3.0,
MarkType::Text { .. } => 0.5,
MarkType::Rect { .. } => 1.0,
MarkType::Scatter { .. } => 1.5,
MarkType::BoxPlot { .. } => 2.5,
MarkType::Violin { .. } => 3.0,
MarkType::Heatmap { .. } => 2.0,
MarkType::Histogram { .. } => 1.5,
MarkType::Density { .. } => 2.5,
MarkType::Contour { .. } => 3.5,
MarkType::Radar { .. } => 2.0,
MarkType::Sankey { .. } => 4.0,
MarkType::Treemap { .. } => 3.0,
MarkType::Composite(ref marks) => marks.iter().map(|m| m.complexity()).sum(),
MarkType::Point3D { .. } => 4.0,
MarkType::Surface3D { .. } => 5.0,
MarkType::Choropleth { .. } => 3.5,
MarkType::NetworkGraph { .. } => 4.5,
MarkType::DotMap { .. } => 3.0,
MarkType::FlowMap { .. } => 4.0,
};
let encoding_complexity = self.encoding.complexity();
let transform_complexity = self.transform.len() as f64 * 0.5;
let intelligence_complexity = self.intelligence.as_ref().map_or(0.0, |i| i.complexity());
base_complexity + encoding_complexity + transform_complexity + intelligence_complexity
}
}