1pub mod expr;
2pub mod mapping;
3
4pub use mapping::{apply_after_stat, resolve_mappings};
5
6#[derive(Clone, Debug, PartialEq, Eq, Hash)]
8pub enum Aesthetic {
9 X,
10 Y,
11 Color,
12 Fill,
13 Size,
14 Shape,
15 Alpha,
16 Linetype,
17 Group,
18 Ymin,
19 Ymax,
20 Xmin,
21 Xmax,
22 Label,
23 Weight,
24 Xend,
25 Yend,
26 Angle,
27 Radius,
28 Open,
31 High,
32 Low,
33 Close,
34 Xintercept,
38 Yintercept,
39 Slope,
40 Intercept,
41}
42
43impl Aesthetic {
44 pub fn col_name(&self) -> &str {
46 match self {
47 Aesthetic::X => "x",
48 Aesthetic::Y => "y",
49 Aesthetic::Color => "color",
50 Aesthetic::Fill => "fill",
51 Aesthetic::Size => "size",
52 Aesthetic::Shape => "shape",
53 Aesthetic::Alpha => "alpha",
54 Aesthetic::Linetype => "linetype",
55 Aesthetic::Group => "group",
56 Aesthetic::Ymin => "ymin",
57 Aesthetic::Ymax => "ymax",
58 Aesthetic::Xmin => "xmin",
59 Aesthetic::Xmax => "xmax",
60 Aesthetic::Label => "label",
61 Aesthetic::Weight => "weight",
62 Aesthetic::Xend => "xend",
63 Aesthetic::Yend => "yend",
64 Aesthetic::Angle => "angle",
65 Aesthetic::Radius => "radius",
66 Aesthetic::Open => "open",
67 Aesthetic::High => "high",
68 Aesthetic::Low => "low",
69 Aesthetic::Close => "close",
70 Aesthetic::Xintercept => "xintercept",
71 Aesthetic::Yintercept => "yintercept",
72 Aesthetic::Slope => "slope",
73 Aesthetic::Intercept => "intercept",
74 }
75 }
76}
77
78#[derive(Clone, Debug, PartialEq)]
80pub enum MappingStage {
81 BeforeStat,
83 AfterStat,
85}
86
87#[derive(Clone, Debug)]
89pub struct AesMapping {
90 pub column: String,
91 pub aesthetic: Aesthetic,
92 pub stage: MappingStage,
93}
94
95#[derive(Clone, Debug)]
99pub struct AfterScaleSpec {
100 pub target: Aesthetic,
101 pub source: Aesthetic,
102 pub lightness: f64,
103}
104
105#[derive(Clone, Debug, Default)]
107pub struct Aes {
108 pub mappings: Vec<AesMapping>,
109 pub after_scale: Vec<AfterScaleSpec>,
111}
112
113impl Aes {
114 pub fn new() -> Self {
115 Self::default()
116 }
117
118 fn push(mut self, col: &str, aesthetic: Aesthetic) -> Self {
119 self.mappings.push(AesMapping {
120 column: col.to_string(),
121 aesthetic,
122 stage: MappingStage::BeforeStat,
123 });
124 self
125 }
126
127 fn push_after_stat(mut self, col: &str, aesthetic: Aesthetic) -> Self {
128 self.mappings.push(AesMapping {
129 column: col.to_string(),
130 aesthetic,
131 stage: MappingStage::AfterStat,
132 });
133 self
134 }
135
136 pub fn after_scale_fill_from_color(mut self, lightness: f64) -> Self {
142 self.after_scale.push(AfterScaleSpec {
143 target: Aesthetic::Fill,
144 source: Aesthetic::Color,
145 lightness,
146 });
147 self
148 }
149
150 pub fn after_scale_color_from_fill(mut self, lightness: f64) -> Self {
153 self.after_scale.push(AfterScaleSpec {
154 target: Aesthetic::Color,
155 source: Aesthetic::Fill,
156 lightness,
157 });
158 self
159 }
160
161 pub fn stage(mut self, aesthetic: Aesthetic, start: &str, after_stat: &str) -> Self {
164 self.mappings.push(AesMapping {
165 column: start.to_string(),
166 aesthetic: aesthetic.clone(),
167 stage: MappingStage::BeforeStat,
168 });
169 self.mappings.push(AesMapping {
170 column: after_stat.to_string(),
171 aesthetic,
172 stage: MappingStage::AfterStat,
173 });
174 self
175 }
176
177 pub fn x(self, col: &str) -> Self {
178 self.push(col, Aesthetic::X)
179 }
180 pub fn y(self, col: &str) -> Self {
181 self.push(col, Aesthetic::Y)
182 }
183 pub fn color(self, col: &str) -> Self {
184 self.push(col, Aesthetic::Color)
185 }
186 pub fn fill(self, col: &str) -> Self {
187 self.push(col, Aesthetic::Fill)
188 }
189 pub fn size(self, col: &str) -> Self {
190 self.push(col, Aesthetic::Size)
191 }
192 pub fn shape(self, col: &str) -> Self {
193 self.push(col, Aesthetic::Shape)
194 }
195 pub fn alpha(self, col: &str) -> Self {
196 self.push(col, Aesthetic::Alpha)
197 }
198 pub fn group(self, col: &str) -> Self {
199 self.push(col, Aesthetic::Group)
200 }
201 pub fn ymin(self, col: &str) -> Self {
202 self.push(col, Aesthetic::Ymin)
203 }
204 pub fn ymax(self, col: &str) -> Self {
205 self.push(col, Aesthetic::Ymax)
206 }
207 pub fn label(self, col: &str) -> Self {
208 self.push(col, Aesthetic::Label)
209 }
210 pub fn weight(self, col: &str) -> Self {
211 self.push(col, Aesthetic::Weight)
212 }
213 pub fn xend(self, col: &str) -> Self {
214 self.push(col, Aesthetic::Xend)
215 }
216 pub fn yend(self, col: &str) -> Self {
217 self.push(col, Aesthetic::Yend)
218 }
219 pub fn xmin(self, col: &str) -> Self {
220 self.push(col, Aesthetic::Xmin)
221 }
222 pub fn xmax(self, col: &str) -> Self {
223 self.push(col, Aesthetic::Xmax)
224 }
225 pub fn angle(self, col: &str) -> Self {
226 self.push(col, Aesthetic::Angle)
227 }
228 pub fn radius(self, col: &str) -> Self {
229 self.push(col, Aesthetic::Radius)
230 }
231 pub fn linetype(self, col: &str) -> Self {
232 self.push(col, Aesthetic::Linetype)
233 }
234 pub fn open(self, col: &str) -> Self {
236 self.push(col, Aesthetic::Open)
237 }
238 pub fn high(self, col: &str) -> Self {
240 self.push(col, Aesthetic::High)
241 }
242 pub fn low(self, col: &str) -> Self {
244 self.push(col, Aesthetic::Low)
245 }
246 pub fn close(self, col: &str) -> Self {
248 self.push(col, Aesthetic::Close)
249 }
250 pub fn xintercept(self, col: &str) -> Self {
252 self.push(col, Aesthetic::Xintercept)
253 }
254 pub fn yintercept(self, col: &str) -> Self {
256 self.push(col, Aesthetic::Yintercept)
257 }
258 pub fn slope(self, col: &str) -> Self {
260 self.push(col, Aesthetic::Slope)
261 }
262 pub fn intercept(self, col: &str) -> Self {
264 self.push(col, Aesthetic::Intercept)
265 }
266
267 pub fn after_stat_y(self, col: &str) -> Self {
273 self.push_after_stat(col, Aesthetic::Y)
274 }
275
276 pub fn after_stat_x(self, col: &str) -> Self {
278 self.push_after_stat(col, Aesthetic::X)
279 }
280
281 pub fn after_stat_fill(self, col: &str) -> Self {
283 self.push_after_stat(col, Aesthetic::Fill)
284 }
285
286 pub fn after_stat_color(self, col: &str) -> Self {
288 self.push_after_stat(col, Aesthetic::Color)
289 }
290
291 pub fn after_stat_size(self, col: &str) -> Self {
293 self.push_after_stat(col, Aesthetic::Size)
294 }
295
296 pub fn after_stat_alpha(self, col: &str) -> Self {
298 self.push_after_stat(col, Aesthetic::Alpha)
299 }
300
301 pub fn get_mapping(&self, aes: &Aesthetic) -> Option<&str> {
303 self.mappings
304 .iter()
305 .find(|m| m.aesthetic == *aes)
306 .map(|m| m.column.as_str())
307 }
308
309 pub fn merge(&self, other: &Aes) -> Aes {
311 let mut result = self.clone();
312 for m in &other.mappings {
313 result
315 .mappings
316 .retain(|existing| existing.aesthetic != m.aesthetic);
317 result.mappings.push(m.clone());
318 }
319 result
320 }
321}