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}
29
30impl Aesthetic {
31 pub fn col_name(&self) -> &str {
33 match self {
34 Aesthetic::X => "x",
35 Aesthetic::Y => "y",
36 Aesthetic::Color => "color",
37 Aesthetic::Fill => "fill",
38 Aesthetic::Size => "size",
39 Aesthetic::Shape => "shape",
40 Aesthetic::Alpha => "alpha",
41 Aesthetic::Linetype => "linetype",
42 Aesthetic::Group => "group",
43 Aesthetic::Ymin => "ymin",
44 Aesthetic::Ymax => "ymax",
45 Aesthetic::Xmin => "xmin",
46 Aesthetic::Xmax => "xmax",
47 Aesthetic::Label => "label",
48 Aesthetic::Weight => "weight",
49 Aesthetic::Xend => "xend",
50 Aesthetic::Yend => "yend",
51 Aesthetic::Angle => "angle",
52 Aesthetic::Radius => "radius",
53 }
54 }
55}
56
57#[derive(Clone, Debug, PartialEq)]
59pub enum MappingStage {
60 BeforeStat,
62 AfterStat,
64}
65
66#[derive(Clone, Debug)]
68pub struct AesMapping {
69 pub column: String,
70 pub aesthetic: Aesthetic,
71 pub stage: MappingStage,
72}
73
74#[derive(Clone, Debug)]
78pub struct AfterScaleSpec {
79 pub target: Aesthetic,
80 pub source: Aesthetic,
81 pub lightness: f64,
82}
83
84#[derive(Clone, Debug, Default)]
86pub struct Aes {
87 pub mappings: Vec<AesMapping>,
88 pub after_scale: Vec<AfterScaleSpec>,
90}
91
92impl Aes {
93 pub fn new() -> Self {
94 Self::default()
95 }
96
97 fn push(mut self, col: &str, aesthetic: Aesthetic) -> Self {
98 self.mappings.push(AesMapping {
99 column: col.to_string(),
100 aesthetic,
101 stage: MappingStage::BeforeStat,
102 });
103 self
104 }
105
106 fn push_after_stat(mut self, col: &str, aesthetic: Aesthetic) -> Self {
107 self.mappings.push(AesMapping {
108 column: col.to_string(),
109 aesthetic,
110 stage: MappingStage::AfterStat,
111 });
112 self
113 }
114
115 pub fn after_scale_fill_from_color(mut self, lightness: f64) -> Self {
121 self.after_scale.push(AfterScaleSpec {
122 target: Aesthetic::Fill,
123 source: Aesthetic::Color,
124 lightness,
125 });
126 self
127 }
128
129 pub fn after_scale_color_from_fill(mut self, lightness: f64) -> Self {
132 self.after_scale.push(AfterScaleSpec {
133 target: Aesthetic::Color,
134 source: Aesthetic::Fill,
135 lightness,
136 });
137 self
138 }
139
140 pub fn stage(mut self, aesthetic: Aesthetic, start: &str, after_stat: &str) -> Self {
143 self.mappings.push(AesMapping {
144 column: start.to_string(),
145 aesthetic: aesthetic.clone(),
146 stage: MappingStage::BeforeStat,
147 });
148 self.mappings.push(AesMapping {
149 column: after_stat.to_string(),
150 aesthetic,
151 stage: MappingStage::AfterStat,
152 });
153 self
154 }
155
156 pub fn x(self, col: &str) -> Self {
157 self.push(col, Aesthetic::X)
158 }
159 pub fn y(self, col: &str) -> Self {
160 self.push(col, Aesthetic::Y)
161 }
162 pub fn color(self, col: &str) -> Self {
163 self.push(col, Aesthetic::Color)
164 }
165 pub fn fill(self, col: &str) -> Self {
166 self.push(col, Aesthetic::Fill)
167 }
168 pub fn size(self, col: &str) -> Self {
169 self.push(col, Aesthetic::Size)
170 }
171 pub fn shape(self, col: &str) -> Self {
172 self.push(col, Aesthetic::Shape)
173 }
174 pub fn alpha(self, col: &str) -> Self {
175 self.push(col, Aesthetic::Alpha)
176 }
177 pub fn group(self, col: &str) -> Self {
178 self.push(col, Aesthetic::Group)
179 }
180 pub fn ymin(self, col: &str) -> Self {
181 self.push(col, Aesthetic::Ymin)
182 }
183 pub fn ymax(self, col: &str) -> Self {
184 self.push(col, Aesthetic::Ymax)
185 }
186 pub fn label(self, col: &str) -> Self {
187 self.push(col, Aesthetic::Label)
188 }
189 pub fn weight(self, col: &str) -> Self {
190 self.push(col, Aesthetic::Weight)
191 }
192 pub fn xend(self, col: &str) -> Self {
193 self.push(col, Aesthetic::Xend)
194 }
195 pub fn yend(self, col: &str) -> Self {
196 self.push(col, Aesthetic::Yend)
197 }
198 pub fn xmin(self, col: &str) -> Self {
199 self.push(col, Aesthetic::Xmin)
200 }
201 pub fn xmax(self, col: &str) -> Self {
202 self.push(col, Aesthetic::Xmax)
203 }
204 pub fn angle(self, col: &str) -> Self {
205 self.push(col, Aesthetic::Angle)
206 }
207 pub fn radius(self, col: &str) -> Self {
208 self.push(col, Aesthetic::Radius)
209 }
210 pub fn linetype(self, col: &str) -> Self {
211 self.push(col, Aesthetic::Linetype)
212 }
213
214 pub fn after_stat_y(self, col: &str) -> Self {
220 self.push_after_stat(col, Aesthetic::Y)
221 }
222
223 pub fn after_stat_x(self, col: &str) -> Self {
225 self.push_after_stat(col, Aesthetic::X)
226 }
227
228 pub fn after_stat_fill(self, col: &str) -> Self {
230 self.push_after_stat(col, Aesthetic::Fill)
231 }
232
233 pub fn after_stat_color(self, col: &str) -> Self {
235 self.push_after_stat(col, Aesthetic::Color)
236 }
237
238 pub fn after_stat_size(self, col: &str) -> Self {
240 self.push_after_stat(col, Aesthetic::Size)
241 }
242
243 pub fn after_stat_alpha(self, col: &str) -> Self {
245 self.push_after_stat(col, Aesthetic::Alpha)
246 }
247
248 pub fn get_mapping(&self, aes: &Aesthetic) -> Option<&str> {
250 self.mappings
251 .iter()
252 .find(|m| m.aesthetic == *aes)
253 .map(|m| m.column.as_str())
254 }
255
256 pub fn merge(&self, other: &Aes) -> Aes {
258 let mut result = self.clone();
259 for m in &other.mappings {
260 result
262 .mappings
263 .retain(|existing| existing.aesthetic != m.aesthetic);
264 result.mappings.push(m.clone());
265 }
266 result
267 }
268}