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}
35
36impl Aesthetic {
37 pub fn col_name(&self) -> &str {
39 match self {
40 Aesthetic::X => "x",
41 Aesthetic::Y => "y",
42 Aesthetic::Color => "color",
43 Aesthetic::Fill => "fill",
44 Aesthetic::Size => "size",
45 Aesthetic::Shape => "shape",
46 Aesthetic::Alpha => "alpha",
47 Aesthetic::Linetype => "linetype",
48 Aesthetic::Group => "group",
49 Aesthetic::Ymin => "ymin",
50 Aesthetic::Ymax => "ymax",
51 Aesthetic::Xmin => "xmin",
52 Aesthetic::Xmax => "xmax",
53 Aesthetic::Label => "label",
54 Aesthetic::Weight => "weight",
55 Aesthetic::Xend => "xend",
56 Aesthetic::Yend => "yend",
57 Aesthetic::Angle => "angle",
58 Aesthetic::Radius => "radius",
59 Aesthetic::Open => "open",
60 Aesthetic::High => "high",
61 Aesthetic::Low => "low",
62 Aesthetic::Close => "close",
63 }
64 }
65}
66
67#[derive(Clone, Debug, PartialEq)]
69pub enum MappingStage {
70 BeforeStat,
72 AfterStat,
74}
75
76#[derive(Clone, Debug)]
78pub struct AesMapping {
79 pub column: String,
80 pub aesthetic: Aesthetic,
81 pub stage: MappingStage,
82}
83
84#[derive(Clone, Debug)]
88pub struct AfterScaleSpec {
89 pub target: Aesthetic,
90 pub source: Aesthetic,
91 pub lightness: f64,
92}
93
94#[derive(Clone, Debug, Default)]
96pub struct Aes {
97 pub mappings: Vec<AesMapping>,
98 pub after_scale: Vec<AfterScaleSpec>,
100}
101
102impl Aes {
103 pub fn new() -> Self {
104 Self::default()
105 }
106
107 fn push(mut self, col: &str, aesthetic: Aesthetic) -> Self {
108 self.mappings.push(AesMapping {
109 column: col.to_string(),
110 aesthetic,
111 stage: MappingStage::BeforeStat,
112 });
113 self
114 }
115
116 fn push_after_stat(mut self, col: &str, aesthetic: Aesthetic) -> Self {
117 self.mappings.push(AesMapping {
118 column: col.to_string(),
119 aesthetic,
120 stage: MappingStage::AfterStat,
121 });
122 self
123 }
124
125 pub fn after_scale_fill_from_color(mut self, lightness: f64) -> Self {
131 self.after_scale.push(AfterScaleSpec {
132 target: Aesthetic::Fill,
133 source: Aesthetic::Color,
134 lightness,
135 });
136 self
137 }
138
139 pub fn after_scale_color_from_fill(mut self, lightness: f64) -> Self {
142 self.after_scale.push(AfterScaleSpec {
143 target: Aesthetic::Color,
144 source: Aesthetic::Fill,
145 lightness,
146 });
147 self
148 }
149
150 pub fn stage(mut self, aesthetic: Aesthetic, start: &str, after_stat: &str) -> Self {
153 self.mappings.push(AesMapping {
154 column: start.to_string(),
155 aesthetic: aesthetic.clone(),
156 stage: MappingStage::BeforeStat,
157 });
158 self.mappings.push(AesMapping {
159 column: after_stat.to_string(),
160 aesthetic,
161 stage: MappingStage::AfterStat,
162 });
163 self
164 }
165
166 pub fn x(self, col: &str) -> Self {
167 self.push(col, Aesthetic::X)
168 }
169 pub fn y(self, col: &str) -> Self {
170 self.push(col, Aesthetic::Y)
171 }
172 pub fn color(self, col: &str) -> Self {
173 self.push(col, Aesthetic::Color)
174 }
175 pub fn fill(self, col: &str) -> Self {
176 self.push(col, Aesthetic::Fill)
177 }
178 pub fn size(self, col: &str) -> Self {
179 self.push(col, Aesthetic::Size)
180 }
181 pub fn shape(self, col: &str) -> Self {
182 self.push(col, Aesthetic::Shape)
183 }
184 pub fn alpha(self, col: &str) -> Self {
185 self.push(col, Aesthetic::Alpha)
186 }
187 pub fn group(self, col: &str) -> Self {
188 self.push(col, Aesthetic::Group)
189 }
190 pub fn ymin(self, col: &str) -> Self {
191 self.push(col, Aesthetic::Ymin)
192 }
193 pub fn ymax(self, col: &str) -> Self {
194 self.push(col, Aesthetic::Ymax)
195 }
196 pub fn label(self, col: &str) -> Self {
197 self.push(col, Aesthetic::Label)
198 }
199 pub fn weight(self, col: &str) -> Self {
200 self.push(col, Aesthetic::Weight)
201 }
202 pub fn xend(self, col: &str) -> Self {
203 self.push(col, Aesthetic::Xend)
204 }
205 pub fn yend(self, col: &str) -> Self {
206 self.push(col, Aesthetic::Yend)
207 }
208 pub fn xmin(self, col: &str) -> Self {
209 self.push(col, Aesthetic::Xmin)
210 }
211 pub fn xmax(self, col: &str) -> Self {
212 self.push(col, Aesthetic::Xmax)
213 }
214 pub fn angle(self, col: &str) -> Self {
215 self.push(col, Aesthetic::Angle)
216 }
217 pub fn radius(self, col: &str) -> Self {
218 self.push(col, Aesthetic::Radius)
219 }
220 pub fn linetype(self, col: &str) -> Self {
221 self.push(col, Aesthetic::Linetype)
222 }
223 pub fn open(self, col: &str) -> Self {
225 self.push(col, Aesthetic::Open)
226 }
227 pub fn high(self, col: &str) -> Self {
229 self.push(col, Aesthetic::High)
230 }
231 pub fn low(self, col: &str) -> Self {
233 self.push(col, Aesthetic::Low)
234 }
235 pub fn close(self, col: &str) -> Self {
237 self.push(col, Aesthetic::Close)
238 }
239
240 pub fn after_stat_y(self, col: &str) -> Self {
246 self.push_after_stat(col, Aesthetic::Y)
247 }
248
249 pub fn after_stat_x(self, col: &str) -> Self {
251 self.push_after_stat(col, Aesthetic::X)
252 }
253
254 pub fn after_stat_fill(self, col: &str) -> Self {
256 self.push_after_stat(col, Aesthetic::Fill)
257 }
258
259 pub fn after_stat_color(self, col: &str) -> Self {
261 self.push_after_stat(col, Aesthetic::Color)
262 }
263
264 pub fn after_stat_size(self, col: &str) -> Self {
266 self.push_after_stat(col, Aesthetic::Size)
267 }
268
269 pub fn after_stat_alpha(self, col: &str) -> Self {
271 self.push_after_stat(col, Aesthetic::Alpha)
272 }
273
274 pub fn get_mapping(&self, aes: &Aesthetic) -> Option<&str> {
276 self.mappings
277 .iter()
278 .find(|m| m.aesthetic == *aes)
279 .map(|m| m.column.as_str())
280 }
281
282 pub fn merge(&self, other: &Aes) -> Aes {
284 let mut result = self.clone();
285 for m in &other.mappings {
286 result
288 .mappings
289 .retain(|existing| existing.aesthetic != m.aesthetic);
290 result.mappings.push(m.clone());
291 }
292 result
293 }
294}