Skip to main content

chart_js_rs/
lib.rs

1#![allow(non_snake_case)]
2#![doc = include_str!("../README.md")]
3
4pub mod bar;
5pub mod coordinate;
6pub mod doughnut;
7pub mod exports;
8pub mod functions;
9pub mod objects;
10pub mod pie;
11pub mod scatter;
12pub mod traits;
13
14#[cfg(feature = "workers")]
15pub mod worker;
16
17pub use objects::*;
18pub use traits::*;
19pub use utils::*;
20
21#[cfg(feature = "workers")]
22pub use worker_chart::*;
23
24#[doc(hidden)]
25mod utils;
26
27use exports::get_chart;
28use gloo_utils::format::JsValueSerdeExt;
29use serde::Deserialize;
30
31#[cfg(feature = "workers")]
32use wasm_bindgen::{self, prelude::*};
33
34#[cfg(feature = "workers")]
35use web_sys::WorkerGlobalScope;
36
37pub trait ChartExt: erased_serde::Serialize {
38    type DS;
39
40    fn new(id: impl AsRef<str>) -> Self
41    where
42        Self: Default,
43    {
44        Self::default().id(id.as_ref().into())
45    }
46
47    fn get_id(&self) -> &str;
48    fn id(self, id: String) -> Self
49    where
50        Self: Sized;
51
52    fn get_data(&mut self) -> &mut Self::DS;
53    fn data(mut self, data: impl Into<Self::DS>) -> Self
54    where
55        Self: Sized,
56    {
57        *self.get_data() = data.into();
58        self
59    }
60
61    fn get_options(&mut self) -> &mut ChartOptions;
62    fn options(mut self, options: impl Into<ChartOptions>) -> Self
63    where
64        Self: Sized,
65    {
66        *self.get_options() = options.into();
67        self
68    }
69
70    #[allow(clippy::wrong_self_convention)]
71    fn into_json(&self) -> wasm_bindgen::JsValue {
72        let json_value = erased_serde::serialize(self, serde_json::value::Serializer)
73            .expect("Unable to serialize chart!");
74        <wasm_bindgen::JsValue as JsValueSerdeExt>::from_serde(&json_value)
75            .expect("Unable to convert to JsValue!")
76    }
77
78    #[allow(clippy::wrong_self_convention)]
79    fn into_chart(&self) -> Chart {
80        Chart {
81            obj: self.into_json(),
82            id: self.get_id().into(),
83            mutate: false,
84            plugins: String::new(),
85            defaults: String::new(),
86        }
87    }
88
89    fn get_chart_from_id(id: &str) -> Option<Self>
90    where
91        for<'de> Self: Deserialize<'de>,
92    {
93        let chart = get_chart(id);
94
95        serde_wasm_bindgen::from_value(chart)
96            .inspect_err(|e| {
97                gloo_console::error!(e.to_string());
98            })
99            .ok()
100    }
101}
102
103#[cfg(feature = "workers")]
104pub fn is_worker() -> bool {
105    js_sys::global().dyn_into::<WorkerGlobalScope>().is_ok()
106}
107
108#[cfg(feature = "workers")]
109mod worker_chart {
110    use crate::*;
111
112    pub trait WorkerChartExt: ChartExt {
113        #[allow(async_fn_in_trait)]
114        #[allow(clippy::wrong_self_convention)]
115        async fn into_worker_chart(
116            &self,
117            imports_block: &str,
118        ) -> Result<WorkerChart, Box<dyn std::error::Error>> {
119            Ok(WorkerChart {
120                obj: self.into_json(),
121                id: self.get_id().into(),
122                mutate: false,
123                plugins: String::new(),
124                defaults: String::new(),
125                worker: crate::worker::ChartWorker::new(imports_block).await?,
126            })
127        }
128    }
129    #[wasm_bindgen]
130    #[derive(Clone)]
131    #[must_use = "\nAppend .render_async()\n"]
132    pub struct WorkerChart {
133        pub(crate) obj: JsValue,
134        pub(crate) id: String,
135        pub(crate) mutate: bool,
136        pub(crate) plugins: String,
137        pub(crate) defaults: String,
138        pub(crate) worker: crate::worker::ChartWorker,
139    }
140    impl WorkerChart {
141        pub async fn render_async(self) -> Result<(), Box<dyn std::error::Error>> {
142            self.worker
143                .render(self.obj, &self.id, self.mutate, self.plugins, self.defaults)
144                .await
145        }
146
147        pub async fn update_async(self, animate: bool) -> Result<bool, Box<dyn std::error::Error>> {
148            self.worker.update(self.obj, &self.id, animate).await
149        }
150
151        #[must_use = "\nAppend .render_async()\n"]
152        pub fn mutate(&mut self) -> Self {
153            self.mutate = true;
154            self.clone()
155        }
156
157        #[must_use = "\nAppend .render_async()\n"]
158        pub fn plugins(&mut self, plugins: impl Into<String>) -> Self {
159            self.plugins = plugins.into();
160            self.clone()
161        }
162
163        #[must_use = "\nAppend .render_async()\n"]
164        pub fn defaults(&mut self, defaults: impl Into<String>) -> Self {
165            self.defaults = format!("{}\n{}", self.defaults, defaults.into());
166            self.to_owned()
167        }
168    }
169}