use std::collections::HashMap;
use std::sync::{Arc, OnceLock};
use crate::plot::geom::{Channel, GeomBuilder};
use crate::plot::scale::LabelFormatter;
use crate::plot::Geom;
use crate::scales::value::DataColumn;
pub type GeomFactory = fn(Option<DataColumn>, HashMap<String, Channel>) -> Box<dyn Geom>;
#[derive(Default)]
pub struct ReadContext {
geoms: HashMap<String, GeomFactory>,
formatters: HashMap<String, Arc<LabelFormatter>>,
}
impl std::fmt::Debug for ReadContext {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
let mut geoms: Vec<&str> = self.geoms.keys().map(String::as_str).collect();
geoms.sort_unstable();
let mut formatters: Vec<&str> = self.formatters.keys().map(String::as_str).collect();
formatters.sort_unstable();
f.debug_struct("ReadContext")
.field("geoms", &geoms)
.field("formatters", &formatters)
.finish()
}
}
impl ReadContext {
pub fn new() -> Self {
let mut out = Self::default();
out.register_builtin_geoms();
out
}
pub fn with_geom(mut self, kind: impl Into<String>, factory: GeomFactory) -> Self {
self.geoms.insert(kind.into(), factory);
self
}
pub fn with_formatter<F>(mut self, name: impl Into<String>, f: F) -> Self
where
F: Fn(&crate::scales::value::Value, &crate::scales::locale::Locale) -> String
+ Send
+ Sync
+ 'static,
{
self.formatters.insert(name.into(), Arc::new(f));
self
}
pub(crate) fn geom_factory(&self, kind: &str) -> Option<GeomFactory> {
self.geoms.get(kind).copied()
}
pub(crate) fn formatter(&self, name: &str) -> Option<Arc<LabelFormatter>> {
self.formatters.get(name).cloned()
}
fn register_builtin_geoms(&mut self) {
use crate::plot::geom::{
BSplineGeom, BuildableGeom, EllipseGeom, GeometryGeom, LineGeom, PointGeom,
PolygonGeom, RectGeom, RibbonBSplineGeom, RibbonGeom, SegmentGeom, TextFitGeom,
TextGeom, TextPathGeom, WedgeGeom,
};
fn build<G: BuildableGeom>(
keys: Option<DataColumn>,
channels: HashMap<String, Channel>,
) -> Box<dyn Geom> {
Box::new(G::build_from(GeomBuilder::from_parts(keys, channels)))
}
macro_rules! register {
($($tag:literal => $ty:ty),+ $(,)?) => {
$( self.geoms.insert($tag.to_string(), build::<$ty> as GeomFactory); )+
};
}
register! {
"point" => PointGeom,
"line" => LineGeom,
"bspline" => BSplineGeom,
"segment" => SegmentGeom,
"rect" => RectGeom,
"ellipse" => EllipseGeom,
"polygon" => PolygonGeom,
"ribbon" => RibbonGeom,
"ribbon-bspline" => RibbonBSplineGeom,
"wedge" => WedgeGeom,
"geometry" => GeometryGeom,
"text" => TextGeom,
"text-fit" => TextFitGeom,
"text-path" => TextPathGeom,
}
}
}
pub(crate) fn default_context() -> &'static ReadContext {
static CTX: OnceLock<ReadContext> = OnceLock::new();
CTX.get_or_init(ReadContext::new)
}