mod area;
mod bars;
mod categories;
mod cells;
mod line;
mod points;
mod range;
mod rule;
mod text;
pub use area::Area;
pub use bars::Bars;
pub(crate) use bars::Placement;
pub(crate) use categories::Categories;
pub use cells::Cells;
pub(crate) use line::Source;
pub use line::{Dash, Line, LineStyle};
pub use points::{PointStyle, Points};
pub use range::Range;
pub(crate) use range::RangePlacement;
pub(crate) use rule::Orientation;
pub use rule::Rule;
pub use text::{Align, Text};
#[non_exhaustive]
#[derive(Debug, Clone)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub enum Mark<'a> {
Line(Line<'a>),
Points(Points<'a>),
Bars(Bars<'a>),
Area(Area<'a>),
Cells(Cells<'a>),
Range(Range<'a>),
Rule(Rule),
Text(Text),
}
impl<'a> Mark<'a> {
pub fn into_owned(self) -> Mark<'static> {
match self {
Mark::Line(line) => Mark::Line(line.into_owned()),
Mark::Points(points) => Mark::Points(points.into_owned()),
Mark::Bars(bars) => Mark::Bars(bars.into_owned()),
Mark::Area(area) => Mark::Area(area.into_owned()),
Mark::Cells(cells) => Mark::Cells(cells.into_owned()),
Mark::Range(range) => Mark::Range(range.into_owned()),
Mark::Rule(rule) => Mark::Rule(rule),
Mark::Text(text) => Mark::Text(text),
}
}
pub(crate) fn validate(&self) -> Result<(), crate::Error> {
match self {
Mark::Line(mark) => mark.validate(),
Mark::Points(mark) => mark.validate(),
Mark::Bars(mark) => mark.validate(),
Mark::Area(mark) => mark.validate(),
Mark::Cells(mark) => mark.validate(),
Mark::Range(mark) => mark.validate(),
Mark::Rule(mark) => mark.validate(),
Mark::Text(mark) => mark.validate(),
}
}
}
pub(crate) fn pair(mark: &'static str, a: usize, b: usize) -> Result<(), crate::Error> {
if a == b {
Ok(())
} else {
Err(crate::Error::UnequalChannels {
mark,
lengths: (a, b),
})
}
}
impl<'a> From<Line<'a>> for Mark<'a> {
fn from(line: Line<'a>) -> Mark<'a> {
Mark::Line(line)
}
}
impl<'a> From<Points<'a>> for Mark<'a> {
fn from(points: Points<'a>) -> Mark<'a> {
Mark::Points(points)
}
}
impl<'a> From<Bars<'a>> for Mark<'a> {
fn from(bars: Bars<'a>) -> Mark<'a> {
Mark::Bars(bars)
}
}
impl<'a> From<Area<'a>> for Mark<'a> {
fn from(area: Area<'a>) -> Mark<'a> {
Mark::Area(area)
}
}
impl<'a> From<Rule> for Mark<'a> {
fn from(rule: Rule) -> Mark<'a> {
Mark::Rule(rule)
}
}
impl<'a> From<Text> for Mark<'a> {
fn from(text: Text) -> Mark<'a> {
Mark::Text(text)
}
}
impl<'a> From<Cells<'a>> for Mark<'a> {
fn from(cells: Cells<'a>) -> Mark<'a> {
Mark::Cells(cells)
}
}
impl<'a> From<Range<'a>> for Mark<'a> {
fn from(range: Range<'a>) -> Mark<'a> {
Mark::Range(range)
}
}
#[cfg(test)]
mod validation_tests {
use super::*;
#[test]
fn payload_validation_owns_scalar_constructor_invariants() {
let mut bars = Bars::spans(0.0, 1.0, [1.0]);
bars.placement = Placement::Spans {
start: 0.0,
width: 0.0,
};
assert!(matches!(
bars.validate(),
Err(crate::Error::InvalidParameter { .. })
));
let rule = Rule {
orientation: Orientation::Horizontal(f64::NAN),
color: None,
label: None,
dash: Dash::Solid,
};
assert!(matches!(
rule.validate(),
Err(crate::Error::InvalidParameter { .. })
));
let text = Text {
x: f64::INFINITY,
y: 0.0,
text: String::new(),
color: None,
align: Align::Left,
};
assert!(matches!(
text.validate(),
Err(crate::Error::InvalidParameter { .. })
));
let mut line = Line::function(0.0..1.0, f64::sin);
let Source::Function { domain, .. } = &mut line.source else {
unreachable!("the test constructed a function line")
};
*domain = (1.0, 1.0);
assert!(matches!(
line.validate(),
Err(crate::Error::InvalidParameter { .. })
));
}
}