1use macroex::{FromMacro, Either4, HexNumber, proc_macro2::{Span, Ident, TokenTree}, Error, bail, NumberList, Splat};
2
3fn hex(a: u8, span: Span) -> Result<u8, Error> {
4 Ok(match a {
5 b'0'..= b'9' => a - b'0',
6 b'a'..= b'z' => a - b'a' + 10,
7 b'A'..= b'Z' => a - b'A' + 10,
8 _ => bail!(span, "Not a valid hexadecial number.")
9 })
10}
11
12fn hex2(a: u8, b: u8, span: Span) -> Result<u8, Error> {
13 Ok((hex(a, span)? << 4) + hex(b, span)?)
14}
15
16
17fn parse_slice(lit: &[u8], span: Span) -> Result<[u8; 4], Error>{
18 Ok(match lit.len() {
19 3 => [
20 hex(lit[0], span)? * 17,
21 hex(lit[1], span)? * 17,
22 hex(lit[2], span)? * 17,
23 255
24 ],
25 4 => [
26 hex(lit[0], span)? * 17,
27 hex(lit[1], span)? * 17,
28 hex(lit[2], span)? * 17,
29 hex(lit[3], span)? * 17,
30 ],
31 6 => [
32 hex2(lit[0], lit[1], span)?,
33 hex2(lit[2], lit[3], span)?,
34 hex2(lit[4], lit[5], span)?,
35 255
36 ],
37 8 => [
38 hex2(lit[0], lit[1], span)?,
39 hex2(lit[2], lit[3], span)?,
40 hex2(lit[4], lit[5], span)?,
41 hex2(lit[6], lit[7], span)?,
42 ],
43 _ => bail!(span, "Invalid color syntax, must be of length 3, 4, 6 or 8."),
44 })
45}
46
47fn f2i(floats: [f32; 4]) -> [u8; 4] {
48 [
49 (floats[0] * 255.0) as u8,
50 (floats[1] * 255.0) as u8,
51 (floats[2] * 255.0) as u8,
52 (floats[3] * 255.0) as u8,
53 ]
54}
55
56fn smart_i2f(floats: [u8; 4]) -> [f32; 4] {
57 if floats.iter().all(|x| *x == 0 || *x == 1) {
58 [
59 floats[0] as f32,
60 floats[1] as f32,
61 floats[2] as f32,
62 floats[3] as f32,
63 ]
64 } else {
65 [
66 floats[0] as f32 / 255.0,
67 floats[1] as f32 / 255.0,
68 floats[2] as f32 / 255.0,
69 floats[3] as f32 / 255.0,
70 ]
71 }
72}
73
74fn i2f(floats: [u8; 4]) -> [f32; 4] {
75 [
76 floats[0] as f32 / 255.0,
77 floats[1] as f32 / 255.0,
78 floats[2] as f32 / 255.0,
79 floats[3] as f32 / 255.0,
80 ]
81}
82
83#[derive(Debug, Default, PartialEq, Eq, Hash)]
100pub struct Rgba<T>(pub T);
101
102fn parse_color_name(name: &str, span: Span) -> Result<[u8; 4], Error>{
103 if let Some(num) = name.find(|x| ('0'..='9').contains(&x)) {
104 let (color, right) = name.split_at(num);
105 if let Ok(index) = right.parse() {
106 if let Some(color) = parse_color::parse_tailwind(color, index){
107 Ok(color)
108 } else {
109 bail!(span, "Invalid tailwind color {}-{}", color, index)
110 }
111 } else {
112 bail!(span, "Failed to parse color \"{}\"", name)
113 }
114 } else {
115 match parse_color::parse(&name){
116 Some(x) => Ok(x),
117 None => bail!(span, "Failed to parse color \"{}\"", name),
118 }
119 }
120}
121
122fn padi(arr: [u8; 3]) -> [u8; 4] {
123 [arr[0], arr[1], arr[2], 255]
124}
125
126fn padif(arr: [u8; 3]) -> [u8; 4] {
127 if arr.iter().all(|x| *x == 0 || *x == 1) {
128 [arr[0], arr[1], arr[2], 1]
129 } else {
130 [arr[0], arr[1], arr[2], 255]
131 }
132}
133
134fn padf(arr: [f32; 3]) -> [f32; 4] {
135 [arr[0], arr[1], arr[2], 1.0]
136}
137
138impl FromMacro for Rgba<[u8; 4]> {
139 fn from_one(tt: macroex::proc_macro2::TokenTree) -> Result<Self, macroex::Error> {
140 let span = tt.span();
141 match Either4::from_one(tt)? {
142 Either4::A(Splat(group)) => {
143 let tt = TokenTree::Group(group);
144 match Either4::from_one(tt)? {
145 Either4::A(ints) => Ok(Self(padi(ints))),
146 Either4::B(ints) => Ok(Self(ints)),
147 Either4::C(NumberList(floats)) => Ok(Self(f2i(padf(floats)))),
148 Either4::D(NumberList(floats)) => Ok(Self(f2i(floats))),
149 }
150 },
151 Either4::B(string) => {
152 let string: String = string;
153 if string.starts_with('#') {
154 Ok(Self(parse_slice(&string.as_bytes()[1..], span)?))
155 } else {
156 Ok(Self(parse_slice(&string.as_bytes(), span)?))
157 }
158 },
159 Either4::C(ident) => {
160 let ident: Ident = ident;
161 let name = ident.to_string();
162 Ok(Self(parse_color_name(&name, span)?))
163 },
164 Either4::D(HexNumber(_, hex)) => {
165 Ok(Self(parse_slice(&hex.as_bytes(), span)?))
166 },
167 }
168 }
169}
170
171
172impl FromMacro for Rgba<[f32; 4]> {
173 fn from_one(tt: macroex::proc_macro2::TokenTree) -> Result<Self, macroex::Error> {
174 let span = tt.span();
175 match Either4::from_one(tt)? {
176 Either4::A(Splat(group)) => {
177 let tt = TokenTree::Group(group);
178 match Either4::from_one(tt)? {
179 Either4::A(ints) => Ok(Self(smart_i2f(padif(ints)))),
180 Either4::B(ints) => Ok(Self(smart_i2f(ints))),
181 Either4::C(NumberList(floats)) => Ok(Self(padf(floats))),
182 Either4::D(NumberList(floats)) => Ok(Self(floats)),
183 }
184 },
185 Either4::B(string) => {
186 let string: String = string;
187 if string.starts_with('#') {
188 Ok(Self(i2f(parse_slice(&string.as_bytes()[1..], span)?)))
189 } else {
190 Ok(Self(i2f(parse_slice(&string.as_bytes(), span)?)))
191 }
192 },
193 Either4::C(ident) => {
194 let ident: Ident = ident;
195 let name = ident.to_string();
196 Ok(Self(i2f(parse_color_name(&name, span)?)))
197 },
198 Either4::D(HexNumber(_, hex)) => {
199 Ok(Self(i2f(parse_slice(&hex.as_bytes(), span)?)))
200 },
201 }
202 }
203}