css_colors/
lib.rs

1mod angle;
2mod hsl;
3mod ratio;
4mod rgb;
5
6pub use angle::*;
7pub use hsl::*;
8pub use ratio::*;
9pub use rgb::*;
10
11/// A trait that can be used for converting between different color models
12/// and performing various transformations on them.
13pub trait Color {
14    type Alpha: Color;
15
16    /// Converts `self` to its CSS string format.
17    ///
18    /// # Examples
19    /// ```
20    /// use css_colors::{Color, rgb, rgba};
21    ///
22    /// let salmon = rgb(250, 128, 114);
23    /// let opaque_salmon = rgba(250, 128, 114, 0.50);
24    ///
25    /// assert_eq!(salmon.to_css(), "rgb(250, 128, 114)");
26    /// assert_eq!(opaque_salmon.to_css(), "rgba(250, 128, 114, 0.50)");
27    /// ```
28    fn to_css(self) -> String;
29
30    /// Converts `self` into its RGB representation.
31    /// When converting from a color model that supports an alpha channel
32    /// (e.g. RGBA), the alpha value will not be preserved.
33    ///
34    /// # Examples
35    /// ```
36    /// use css_colors::{Color, rgb, rgba};
37    ///
38    /// let opaque_tomato = rgba(255, 99, 71, 0.5);
39    ///
40    /// assert_eq!(opaque_tomato.to_rgb(), rgb(255, 99, 71));
41    /// ```
42    fn to_rgb(self) -> RGB;
43
44    /// Converts `self` into its RGBA representation.
45    /// When converting from a color model that does not supports an alpha channel
46    /// (e.g. RGB), it will be treated as fully opaque.
47    ///
48    /// # Examples
49    /// ```
50    /// use css_colors::{Color, rgb, rgba};
51    ///
52    /// let tomato = rgb(255, 99, 71);
53    ///
54    /// assert_eq!(tomato.to_rgba(), rgba(255, 99, 71, 1.0));
55    /// ```
56    fn to_rgba(self) -> RGBA;
57
58    /// Converts `self` into its HSL representation.
59    /// When converting from a color model that supports an alpha channel
60    /// (e.g. RGBA), the alpha value will not be preserved.
61    ///
62    /// # Examples
63    /// ```
64    /// use css_colors::{Color, rgb, rgba, hsl};
65    ///
66    /// let tomato = rgb(255, 99, 71);
67    /// let opaque_tomato = rgba(255, 99, 71, 0.5);
68    ///
69    /// assert_eq!(tomato.to_hsl(), hsl(9, 100, 64));
70    /// assert_eq!(opaque_tomato.to_hsl(), hsl(9, 100, 64));
71    /// ```
72    fn to_hsl(self) -> HSL;
73
74    /// Converts `self` into its HSLA representation.
75    /// When converting from a color model that does not supports an alpha channel
76    /// (e.g. RGB), it will be treated as fully opaque.
77    ///
78    /// # Examples
79    /// ```
80    /// use css_colors::{Color, rgb, rgba, hsl, hsla};
81    ///
82    /// let tomato = rgb(255, 99, 71);
83    /// let opaque_tomato = rgba(255, 99, 71, 0.5);
84    ///
85    /// assert_eq!(tomato.to_hsla(), hsla(9, 100, 64, 1.0));
86    /// assert_eq!(opaque_tomato.to_hsla(), hsla(9, 100, 64, 0.5));
87    /// ```
88    fn to_hsla(self) -> HSLA;
89
90    /// Increases the saturation of `self` by an absolute amount.
91    /// Operates on the color within its HSL representation and preserves any existing alpha channel.
92    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-saturate).
93    ///
94    /// # Examples
95    /// ```
96    /// use css_colors::{Color, rgb, hsla, percent};
97    ///
98    /// let salmon = hsla(6, 93, 71, 1.0);
99    /// let cornflower_blue = rgb(100, 149, 237);
100    ///
101    /// assert_eq!(salmon.saturate(percent(7)), hsla(6, 100, 71, 1.0));
102    /// assert_eq!(cornflower_blue.saturate(percent(10)), rgb(92, 146, 246));
103    /// ```
104    fn saturate(self, amount: Ratio) -> Self;
105
106    /// Decreases the saturation of `self` by an absolute amount.
107    /// Operates on the color within its HSL representation and preserves any existing alpha channel.
108    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-desaturate).
109    ///
110    /// # Examples
111    /// ```
112    /// use css_colors::{Color, rgb, rgba, percent};
113    ///
114    /// let tomato = rgba(255, 99, 71, 1.0);
115    /// let cornflower_blue = rgb(100, 149, 237);
116    ///
117    /// assert_eq!(tomato.desaturate(percent(10)), rgba(246, 105, 80, 1.0));
118    /// assert_eq!(cornflower_blue.desaturate(percent(33)), rgb(129, 157, 209));
119    /// ```
120    fn desaturate(self, amount: Ratio) -> Self;
121
122    /// Increases the lightness of `self` by an absolute amount.
123    /// Operates on the color within its HSL representation and preserves any existing alpha channel.
124    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-lighten).
125    ///
126    /// # Examples
127    /// ```
128    /// use css_colors::{Color, rgb, rgba, percent};
129    ///
130    /// let tomato = rgba(255, 99, 71, 1.0);
131    /// let cornflower_blue = rgb(100, 149, 237);
132    ///
133    /// assert_eq!(tomato.lighten(percent(20)), rgba(255, 185, 173, 1.0));
134    /// assert_eq!(cornflower_blue.lighten(percent(33)), rgb(251, 253, 255));
135    /// ```
136    fn lighten(self, amount: Ratio) -> Self;
137
138    /// Decreases the lightness of `self` by an absolute amount.
139    /// Operates on the color within its HSL representation and preserves any existing alpha channel.
140    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-darken).
141    ///
142    /// # Examples
143    /// ```
144    /// use css_colors::{Color, rgb, rgba, percent};
145    ///
146    /// let tomato = rgba(255, 99, 71, 1.0);
147    /// let cornflower_blue = rgb(100, 149, 237);
148    ///
149    /// assert_eq!(tomato.darken(percent(20)), rgba(224, 34, 0, 1.0));
150    /// assert_eq!(cornflower_blue.darken(percent(33)), rgb(18, 65, 152));
151    /// ```
152    fn darken(self, amount: Ratio) -> Self;
153
154    /// Decreases the transparency (or increase the opacity) of `self`, making it more opaque.
155    /// For opqaue colors, converts into the alpha equivalent of `self`, and then increases the opacity.
156    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-fadein).
157    ///
158    /// # Examples
159    /// ```
160    /// use css_colors::{Color, rgb, rgba, percent};
161    ///
162    /// let tomato = rgba(255, 99, 71, 0.25);
163    /// let cornflower_blue = rgb(100, 149, 237);
164    ///
165    /// assert_eq!(tomato.fadein(percent(25)), rgba(255, 99, 71, 0.5));
166    /// assert_eq!(cornflower_blue.fadein(percent(75)), rgba(100, 149, 237, 1.0));
167    /// ```
168    fn fadein(self, amount: Ratio) -> Self::Alpha;
169
170    /// Increases the transparency (or decrease the opacity) of `self`, making it less opaque.
171    /// For opqaue colors, converts into the alpha equivalent of `self`, and then decreases the opacity.
172    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-fadeout).
173    ///
174    /// # Examples
175    /// ```
176    /// use css_colors::{Color, rgb, rgba, percent};
177    ///
178    /// let tomato = rgba(255, 99, 71, 0.5);
179    /// let cornflower_blue = rgb(100, 149, 237);
180    ///
181    /// assert_eq!(tomato.fadeout(percent(25)), rgba(255, 99, 71, 0.25));
182    /// assert_eq!(cornflower_blue.fadeout(percent(75)), rgba(100, 149, 237, 0.25));
183    /// ```
184    fn fadeout(self, amount: Ratio) -> Self::Alpha;
185
186    /// Sets the absolute opacity of `self`, and returns the alpha equivalent.
187    /// Can be applied to colors whether they already have an opacity value or not.
188    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-fade).
189    ///
190    /// # Examples
191    /// ```
192    /// use css_colors::{Color, rgb, rgba, percent};
193    ///
194    /// let tomato = rgba(255, 99, 71, 0.5);
195    /// let cornflower_blue = rgb(100, 149, 237);
196    ///
197    /// assert_eq!(tomato.fade(percent(25)), rgba(255, 99, 71, 0.25));
198    /// assert_eq!(cornflower_blue.fade(percent(50)), rgba(100, 149, 237, 0.5));
199    /// ```
200    fn fade(self, amount: Ratio) -> Self::Alpha;
201
202    /// Rotate the hue angle of `self` in either direction.
203    /// Returns the appropriate `RGB` representation of the color once it has been spun.
204    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-spin).
205    ///
206    /// # Examples
207    /// ```
208    /// use css_colors::{Color, rgb, hsl, deg};
209    ///
210    /// let red = hsl(10, 90, 50);
211    /// let pink = rgb(243, 13, 90);
212    ///
213    /// assert_eq!(red.spin(deg(30)), hsl(40, 90, 50));
214    /// assert_eq!(pink.spin(deg(-30)), rgb(243, 13, 205));
215    /// ```
216    fn spin(self, amount: Angle) -> Self;
217
218    /// Mixes two colors (`self` and any other `Color`) together in variable proportion.
219    /// Takes opacity into account in the calculations.
220    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-mix).
221    ///
222    /// # Examples
223    /// ```
224    /// use css_colors::{Color, rgb, rgba, hsl, hsla, percent};
225    ///
226    /// let red = hsl(10, 90, 50);
227    /// let golden = rgb(243, 166, 13);
228    /// let navy = rgba(0, 0, 80, 1.0);
229    ///
230    /// assert_eq!(red.mix(navy, percent(50)).to_string(), "hsla(347, 65%, 29%, 1.00)");
231    /// assert_eq!(golden.mix(navy, percent(25)), rgba(61, 42, 63, 1.0));
232    /// ```
233    fn mix<T: Color>(self, other: T, weight: Ratio) -> Self::Alpha;
234
235    /// Mixes `self` with white in variable proportion.
236    /// Equivalent to calling `mix()` with `white` (`rgb(255, 255, 255)`).
237    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-tint).
238    ///
239    /// # Examples
240    /// ```
241    /// use css_colors::{Color, rgb, rgba, hsl, percent};
242    ///
243    /// let red = hsl(10, 90, 50);
244    /// let golden = rgb(243, 166, 13);
245    ///
246    /// assert_eq!(red.tint(percent(10)), hsl(10, 92, 95));
247    /// assert_eq!(golden.tint(percent(25)), rgb(252, 233, 194));
248    /// ```
249    fn tint(self, weight: Ratio) -> Self;
250
251    /// Mixes `self` with white in variable proportion.
252    /// Equivalent to calling `mix()` with `black` (`rgb(0, 0, 0)`).
253    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-shade).
254    ///
255    /// # Examples
256    /// ```
257    /// use css_colors::{Color, rgb, rgba, hsl, percent};
258    ///
259    /// let red = hsl(10, 90, 50);
260    /// let golden = rgb(243, 166, 13);
261    ///
262    /// assert_eq!(red.shade(percent(10)), hsl(10, 92, 5));
263    /// assert_eq!(golden.shade(percent(25)), rgb(61, 42, 3));
264    /// ```
265    fn shade(self, weight: Ratio) -> Self;
266
267    /// Remove all saturation from `self` in the HSL color space.
268    /// Equivalent to calling `desaturate(0)` on a color.
269    /// For more, see Less' [Color Operations](http://lesscss.org/functions/#color-operations-greyscale).
270    ///
271    /// # Examples
272    /// ```
273    /// use css_colors::{Color, rgb, rgba};
274    ///
275    /// let tomato = rgba(255, 99, 71, 1.0);
276    /// let cornflower_blue = rgb(100, 149, 237);
277    ///
278    /// assert_eq!(tomato.greyscale(), rgba(163, 163, 163, 1.0));
279    /// assert_eq!(cornflower_blue.greyscale(), rgb(169, 169, 169));
280    /// ```
281    fn greyscale(self) -> Self;
282}
283
284#[cfg(test)]
285mod css_color_tests {
286    use angle::*;
287    use ratio::*;
288    use {hsl, hsla, rgb, rgba, Angle, Color, Ratio, HSL, HSLA, RGB, RGBA};
289
290    pub trait ApproximatelyEq {
291        fn approximately_eq(self, other: Self) -> bool;
292    }
293
294    impl ApproximatelyEq for u8 {
295        fn approximately_eq(self, other: Self) -> bool {
296            self == other || self + 1 == other || self - 1 == other
297        }
298    }
299
300    impl ApproximatelyEq for u16 {
301        fn approximately_eq(self, other: Self) -> bool {
302            self == other || self + 1 == other || self - 1 == other
303        }
304    }
305
306    impl ApproximatelyEq for Angle {
307        fn approximately_eq(self, other: Self) -> bool {
308            self.degrees().approximately_eq(other.degrees())
309        }
310    }
311
312    impl ApproximatelyEq for Ratio {
313        fn approximately_eq(self, other: Self) -> bool {
314            self.as_u8().approximately_eq(other.as_u8())
315        }
316    }
317
318    impl ApproximatelyEq for RGB {
319        fn approximately_eq(self, other: Self) -> bool {
320            self.to_css() == other.to_css()
321                || self.r.approximately_eq(other.r)
322                    && self.g.approximately_eq(other.g)
323                    && self.b.approximately_eq(other.b)
324        }
325    }
326
327    impl ApproximatelyEq for RGBA {
328        fn approximately_eq(self, other: Self) -> bool {
329            self.to_css() == other.to_css()
330                || self.r.approximately_eq(other.r)
331                    && self.g.approximately_eq(other.g)
332                    && self.b.approximately_eq(other.b)
333                    && self.a == other.a
334        }
335    }
336
337    impl ApproximatelyEq for HSL {
338        fn approximately_eq(self, other: Self) -> bool {
339            self.to_css() == other.to_css()
340                || self.h.approximately_eq(other.h)
341                    && self
342                        .s
343                        .as_percentage()
344                        .approximately_eq(other.s.as_percentage())
345                    && self
346                        .l
347                        .as_percentage()
348                        .approximately_eq(other.l.as_percentage())
349        }
350    }
351
352    impl ApproximatelyEq for HSLA {
353        fn approximately_eq(self, other: Self) -> bool {
354            self.to_css() == other.to_css()
355                || self.h.approximately_eq(other.h)
356                    && self
357                        .s
358                        .as_percentage()
359                        .approximately_eq(other.s.as_percentage())
360                    && self
361                        .l
362                        .as_percentage()
363                        .approximately_eq(other.l.as_percentage())
364                    && self.a == other.a
365        }
366    }
367
368    #[macro_export]
369    macro_rules! assert_approximately_eq {
370        ($lhs:expr, $rhs:expr) => {
371            let lhs = $lhs;
372            let rhs = $rhs;
373
374            assert!(lhs.approximately_eq(rhs), "lhs: {}, rhs: {}", lhs, rhs);
375        };
376    }
377
378    #[test]
379    fn can_create_color_structs() {
380        assert_eq!(
381            rgb(5, 10, 15),
382            RGB {
383                r: Ratio::from_u8(5),
384                g: Ratio::from_u8(10),
385                b: Ratio::from_u8(15),
386            }
387        );
388        assert_eq!(
389            rgba(5, 10, 15, 1.0),
390            RGBA {
391                r: Ratio::from_u8(5),
392                g: Ratio::from_u8(10),
393                b: Ratio::from_u8(15),
394                a: Ratio::from_u8(255),
395            }
396        );
397        assert_eq!(
398            hsl(6, 93, 71),
399            HSL {
400                h: Angle::new(6),
401                s: Ratio::from_percentage(93),
402                l: Ratio::from_percentage(71)
403            }
404        );
405        assert_eq!(
406            hsla(6, 93, 71, 1.0),
407            HSLA {
408                h: Angle::new(6),
409                s: Ratio::from_percentage(93),
410                l: Ratio::from_percentage(71),
411                a: Ratio::from_u8(255),
412            }
413        );
414    }
415
416    #[macro_use]
417    mod conversions {
418        macro_rules! conversion_test {
419            (
420                $color_name:ident,
421                rgb($r:expr, $g:expr, $b:expr),
422                hsl($h:expr, $s:expr, $l:expr)
423            ) => {
424                mod $color_name {
425                    use super::super::ApproximatelyEq;
426                    use $crate::{hsl, hsla, rgb, rgba, Color};
427
428                    #[test]
429                    fn rgb_to_rgb() {
430                        assert_eq!(rgb($r, $g, $b).to_rgb(), rgb($r, $g, $b));
431                    }
432
433                    #[test]
434                    fn rgb_to_rgba() {
435                        assert_eq!(rgb($r, $g, $b).to_rgba(), rgba($r, $g, $b, 1.0));
436                    }
437
438                    #[test]
439                    fn rgba_to_rgb() {
440                        assert_eq!(rgba($r, $g, $b, 1.0).to_rgb(), rgb($r, $g, $b));
441                        assert_eq!(rgba($r, $g, $b, 0.78).to_rgb(), rgb($r, $g, $b));
442                        assert_eq!(rgba($r, $g, $b, 0.0).to_rgb(), rgb($r, $g, $b));
443                    }
444
445                    #[test]
446                    fn rgba_to_rgba() {
447                        assert_eq!(rgba($r, $g, $b, 1.0).to_rgba(), rgba($r, $g, $b, 1.0));
448
449                        assert_eq!(rgba($r, $g, $b, 0.78).to_rgba(), rgba($r, $g, $b, 0.78));
450
451                        assert_eq!(rgba($r, $g, $b, 0.0).to_rgba(), rgba($r, $g, $b, 0.0));
452                    }
453
454                    #[test]
455                    fn rgb_to_hsl() {
456                        assert_approximately_eq!(rgb($r, $g, $b).to_hsl(), hsl($h, $s, $l));
457                    }
458
459                    #[test]
460                    fn rgb_to_hsla() {
461                        assert_approximately_eq!(rgb($r, $g, $b).to_hsla(), hsla($h, $s, $l, 1.0));
462                    }
463
464                    #[test]
465                    fn rgba_to_hsl() {
466                        assert_approximately_eq!(rgba($r, $g, $b, 1.0).to_hsl(), hsl($h, $s, $l));
467
468                        assert_approximately_eq!(rgba($r, $g, $b, 0.78).to_hsl(), hsl($h, $s, $l));
469
470                        assert_approximately_eq!(rgba($r, $g, $b, 0.0).to_hsl(), hsl($h, $s, $l));
471                    }
472
473                    #[test]
474                    fn rgba_to_hsla() {
475                        assert_approximately_eq!(
476                            rgba($r, $g, $b, 1.0).to_hsla(),
477                            hsla($h, $s, $l, 1.0)
478                        );
479
480                        assert_approximately_eq!(
481                            rgba($r, $g, $b, 0.78).to_hsla(),
482                            hsla($h, $s, $l, 0.78)
483                        );
484
485                        assert_approximately_eq!(
486                            rgba($r, $g, $b, 0.0).to_hsla(),
487                            hsla($h, $s, $l, 0.0)
488                        );
489                    }
490
491                    #[test]
492                    fn hsl_to_hsl() {
493                        assert_eq!(hsl($h, $s, $l).to_hsl(), hsl($h, $s, $l));
494                    }
495
496                    #[test]
497                    fn hsl_to_hsla() {
498                        assert_eq!(hsl($h, $s, $l).to_hsla(), hsla($h, $s, $l, 1.0));
499                    }
500
501                    #[test]
502                    fn hsla_to_hsl() {
503                        assert_eq!(hsla($h, $s, $l, 1.0).to_hsl(), hsl($h, $s, $l));
504
505                        assert_eq!(hsla($h, $s, $l, 0.78).to_hsl(), hsl($h, $s, $l));
506
507                        assert_eq!(hsla($h, $s, $l, 0.0).to_hsl(), hsl($h, $s, $l));
508                    }
509
510                    #[test]
511                    fn hsla_to_hsla() {
512                        assert_eq!(hsla($h, $s, $l, 1.0).to_hsla(), hsla($h, $s, $l, 1.0));
513
514                        assert_eq!(hsla($h, $s, $l, 0.78).to_hsla(), hsla($h, $s, $l, 0.78));
515
516                        assert_eq!(hsla($h, $s, $l, 0.0).to_hsla(), hsla($h, $s, $l, 0.0));
517                    }
518
519                    #[test]
520                    fn hsl_to_rgb() {
521                        assert_approximately_eq!(hsl($h, $s, $l).to_rgb(), rgb($r, $g, $b));
522                    }
523
524                    #[test]
525                    fn hsl_to_rgba() {
526                        assert_approximately_eq!(hsl($h, $s, $l).to_rgba(), rgba($r, $g, $b, 1.0));
527                    }
528
529                    #[test]
530                    fn hsla_to_rgb() {
531                        assert_approximately_eq!(hsla($h, $s, $l, 1.0).to_rgb(), rgb($r, $g, $b));
532
533                        assert_approximately_eq!(hsla($h, $s, $l, 0.78).to_rgb(), rgb($r, $g, $b));
534
535                        assert_approximately_eq!(hsla($h, $s, $l, 0.0).to_rgb(), rgb($r, $g, $b));
536                    }
537
538                    #[test]
539                    fn hsla_to_rgba() {
540                        assert_approximately_eq!(
541                            hsla($h, $s, $l, 1.0).to_rgba(),
542                            rgba($r, $g, $b, 1.0)
543                        );
544
545                        assert_approximately_eq!(
546                            hsla($h, $s, $l, 0.78).to_rgba(),
547                            rgba($r, $g, $b, 0.78)
548                        );
549
550                        assert_approximately_eq!(
551                            hsla($h, $s, $l, 0.0).to_rgba(),
552                            rgba($r, $g, $b, 0.0)
553                        );
554                    }
555                }
556            };
557        }
558
559        conversion_test!(black, rgb(0, 0, 0), hsl(0, 0, 0));
560        conversion_test!(grey, rgb(230, 230, 230), hsl(0, 0, 90));
561        conversion_test!(white, rgb(255, 255, 255), hsl(0, 0, 100));
562        conversion_test!(pink, rgb(253, 216, 229), hsl(339, 90, 92));
563        conversion_test!(brown, rgb(172, 96, 83), hsl(9, 35, 50));
564        conversion_test!(teal, rgb(23, 98, 119), hsl(193, 68, 28));
565        conversion_test!(green, rgb(89, 161, 54), hsl(100, 50, 42));
566        conversion_test!(pale_blue, rgb(148, 189, 209), hsl(200, 40, 70));
567        conversion_test!(mauve, rgb(136, 102, 153), hsl(280, 20, 50));
568        conversion_test!(cherry, rgb(230, 25, 60), hsl(350, 80, 50));
569        conversion_test!(tomato, rgb(255, 99, 71), hsl(9, 100, 64));
570        conversion_test!(light_salmon, rgb(255, 160, 122), hsl(17, 100, 74));
571        conversion_test!(blue_violet, rgb(138, 43, 226), hsl(271, 76, 53));
572        conversion_test!(dark_orange, rgb(255, 140, 0), hsl(33, 100, 50));
573        conversion_test!(deep_pink, rgb(255, 20, 147), hsl(328, 100, 54));
574        conversion_test!(chartreuse, rgb(127, 255, 0), hsl(90, 100, 50));
575    }
576
577    #[test]
578    fn can_saturate() {
579        assert_approximately_eq!(hsl(9, 35, 50).saturate(percent(20)), hsl(9, 55, 50));
580        assert_approximately_eq!(
581            hsla(9, 35, 50, 1.0).saturate(percent(20)),
582            hsla(9, 55, 50, 1.0)
583        );
584
585        assert_approximately_eq!(rgb(172, 96, 83).saturate(percent(20)), rgb(197, 78, 57));
586        assert_approximately_eq!(
587            rgba(172, 96, 83, 1.0).saturate(percent(20)),
588            rgba(197, 78, 57, 1.0)
589        );
590    }
591
592    #[test]
593    fn can_desaturate() {
594        assert_approximately_eq!(hsl(9, 55, 50).desaturate(percent(20)), hsl(9, 35, 50));
595        assert_approximately_eq!(
596            hsla(9, 55, 50, 1.0).desaturate(percent(20)),
597            hsla(9, 35, 50, 1.0)
598        );
599        assert_approximately_eq!(rgb(197, 78, 57).desaturate(percent(20)), rgb(172, 96, 83));
600        assert_approximately_eq!(
601            rgba(197, 78, 57, 1.0).desaturate(percent(20)),
602            rgba(172, 96, 83, 1.0)
603        );
604    }
605
606    #[test]
607    fn can_lighten() {
608        assert_approximately_eq!(hsl(9, 35, 50).lighten(percent(20)), hsl(9, 35, 70));
609        assert_approximately_eq!(
610            hsla(9, 35, 50, 1.0).lighten(percent(20)),
611            hsla(9, 35, 70, 1.0)
612        );
613        assert_approximately_eq!(rgb(172, 96, 83).lighten(percent(20)), rgb(205, 160, 152));
614        assert_approximately_eq!(
615            rgba(172, 96, 83, 1.0).lighten(percent(20)),
616            rgba(205, 160, 152, 1.0)
617        );
618    }
619
620    #[test]
621    fn can_darken() {
622        assert_approximately_eq!(hsl(9, 35, 70).darken(percent(20)), hsl(9, 35, 50));
623        assert_approximately_eq!(
624            hsla(9, 35, 70, 1.0).darken(percent(20)),
625            hsla(9, 35, 50, 1.0)
626        );
627        assert_approximately_eq!(rgb(205, 160, 152).darken(percent(20)), rgb(172, 96, 83));
628        assert_approximately_eq!(
629            rgba(205, 160, 152, 1.0).darken(percent(20)),
630            rgba(172, 96, 83, 1.0)
631        );
632    }
633
634    #[test]
635    fn can_fadein() {
636        assert_approximately_eq!(hsl(9, 35, 50).fadein(percent(25)), hsla(9, 35, 50, 1.0));
637        assert_approximately_eq!(
638            hsla(9, 35, 50, 0.5).fadein(percent(25)),
639            hsla(9, 35, 50, 0.75)
640        );
641        assert_approximately_eq!(rgb(172, 96, 83).fadein(percent(25)), rgba(172, 96, 83, 1.0));
642        assert_approximately_eq!(
643            rgba(172, 96, 83, 0.50).fadein(percent(25)),
644            rgba(172, 96, 83, 0.75)
645        );
646    }
647
648    #[test]
649    fn can_fadeout() {
650        assert_approximately_eq!(hsl(9, 35, 50).fadeout(percent(25)), hsla(9, 35, 50, 0.75));
651        assert_approximately_eq!(
652            rgb(172, 96, 83).fadeout(percent(25)),
653            rgba(172, 96, 83, 0.75)
654        );
655        assert_approximately_eq!(
656            hsla(9, 35, 50, 0.60).fadeout(percent(25)),
657            hsla(9, 35, 50, 0.35)
658        );
659        assert_approximately_eq!(
660            rgba(172, 96, 83, 0.60).fadeout(percent(25)),
661            rgba(172, 96, 83, 0.35)
662        );
663    }
664
665    #[test]
666    fn can_fade() {
667        let faded_color = rgba(23, 98, 119, 0.5);
668
669        assert_approximately_eq!(rgb(23, 98, 119).fade(percent(50)), faded_color);
670        assert_approximately_eq!(rgba(23, 98, 119, 1.0).fade(percent(50)), faded_color);
671        assert_approximately_eq!(hsl(193, 67, 28).fade(percent(50)), faded_color.to_hsla());
672        assert_approximately_eq!(
673            hsla(193, 67, 28, 1.0).fade(percent(50)),
674            faded_color.to_hsla()
675        );
676    }
677
678    #[test]
679    fn can_spin_forward() {
680        assert_approximately_eq!(rgb(75, 207, 23).spin(deg(100)), rgb(23, 136, 207));
681        assert_approximately_eq!(
682            rgba(75, 207, 23, 1.0).spin(deg(100)),
683            rgba(23, 136, 207, 1.0)
684        );
685        assert_approximately_eq!(hsl(10, 90, 50).spin(deg(30)), hsl(40, 90, 50));
686        assert_approximately_eq!(hsla(10, 90, 50, 1.0).spin(deg(30)), hsla(40, 90, 50, 1.0));
687    }
688
689    #[test]
690    fn can_spin_backwards() {
691        assert_approximately_eq!(rgb(75, 207, 23).spin(deg(-100)), rgb(207, 32, 23));
692        assert_approximately_eq!(
693            rgba(75, 207, 23, 1.0).spin(deg(-100)),
694            rgba(207, 32, 23, 1.0)
695        );
696        assert_approximately_eq!(hsl(10, 90, 50).spin(deg(-30)), hsl(340, 90, 50));
697        assert_approximately_eq!(hsla(10, 90, 50, 1.0).spin(deg(-30)), hsla(340, 90, 50, 1.0));
698    }
699
700    #[test]
701    fn can_mix() {
702        let brown_rgba = rgba(50, 50, 0, 1.0);
703        let brown_hsla = hsla(60, 100, 10, 1.0);
704
705        assert_approximately_eq!(
706            rgba(100, 0, 0, 1.0).mix(rgba(0, 100, 0, 1.0), percent(50)),
707            brown_rgba
708        );
709        assert_approximately_eq!(rgb(100, 0, 0).mix(rgb(0, 100, 0), percent(50)), brown_rgba);
710        assert_approximately_eq!(
711            hsl(0, 100, 20).mix(hsl(120, 100, 20), percent(50)),
712            brown_hsla
713        );
714        assert_approximately_eq!(
715            hsla(0, 100, 20, 1.0).mix(hsla(120, 100, 20, 1.0), percent(50)),
716            brown_hsla
717        );
718    }
719
720    #[test]
721    fn can_mix_single_color() {
722        let rgba_red = rgba(100, 0, 0, 1.0);
723        let rgba_green = rgba(0, 100, 0, 0.5);
724        let hsla_red = hsla(120, 100, 20, 1.0);
725        let hsla_green = hsla(0, 100, 20, 0.5);
726
727        assert_approximately_eq!(rgba_red.mix(rgba_green, percent(100)), rgba_red);
728        assert_approximately_eq!(rgba_red.mix(rgba_green, percent(0)), rgba_green);
729        assert_approximately_eq!(rgba_green.mix(rgba_red, percent(100)), rgba_green);
730        assert_approximately_eq!(rgba_green.mix(rgba_red, percent(0)), rgba_red);
731        assert_approximately_eq!(rgba_red.mix(rgba_green, percent(0)), rgba_green);
732
733        assert_approximately_eq!(hsla_red.mix(hsla_green, percent(100)), hsla_red);
734        assert_approximately_eq!(hsla_red.mix(hsla_green, percent(0)), hsla_green);
735        assert_approximately_eq!(hsla_green.mix(hsla_red, percent(100)), hsla_green);
736        assert_approximately_eq!(hsla_green.mix(hsla_red, percent(0)), hsla_red);
737        assert_approximately_eq!(hsla_red.mix(hsla_green, percent(0)), hsla_green);
738    }
739
740    #[test]
741    fn can_mix_with_alpha() {
742        let red_rgba = rgba(100, 0, 0, 1.0);
743        let green_rgba = rgba(0, 100, 0, 0.5);
744        let brown_rgba = rgba(75, 25, 0, 0.75);
745        let green_hsla = hsla(120, 100, 20, 1.0);
746        let red_hsla = hsla(0, 100, 20, 1.0);
747        let brown_hsla = hsla(60, 100, 10, 1.0);
748
749        assert_approximately_eq!(red_rgba.mix(green_rgba, percent(50)), brown_rgba);
750        assert_approximately_eq!(green_rgba.mix(red_rgba, percent(50)), brown_rgba);
751        assert_approximately_eq!(red_hsla.mix(green_hsla, percent(50)), brown_hsla);
752        assert_approximately_eq!(green_hsla.mix(red_hsla, percent(50)), brown_hsla);
753    }
754
755    #[test]
756    fn can_tint() {
757        assert_approximately_eq!(
758            rgba(0, 0, 255, 0.5).tint(percent(50)),
759            rgba(191, 191, 255, 0.75)
760        );
761        assert_approximately_eq!(rgb(0, 0, 255).tint(percent(50)), rgb(128, 128, 255));
762        assert_approximately_eq!(hsl(6, 93, 71).tint(percent(50)), hsl(6, 92, 85));
763        assert_approximately_eq!(
764            hsla(6, 93, 71, 0.5).tint(percent(50)),
765            hsla(6, 95, 93, 0.75)
766        );
767    }
768
769    #[test]
770    fn can_shade() {
771        assert_approximately_eq!(
772            rgba(0, 0, 255, 0.5).shade(percent(50)),
773            rgba(0, 0, 64, 0.75)
774        );
775        assert_approximately_eq!(rgb(0, 0, 255).shade(percent(50)), rgb(0, 0, 128));
776        assert_approximately_eq!(hsl(6, 93, 71).shade(percent(50)), hsl(6, 38, 36));
777        assert_approximately_eq!(
778            hsla(6, 93, 71, 0.5).shade(percent(50)),
779            hsla(7, 38, 18, 0.75)
780        );
781    }
782
783    #[test]
784    fn can_greyscale() {
785        assert_approximately_eq!(rgb(128, 242, 13).greyscale(), rgb(128, 128, 128));
786        assert_approximately_eq!(
787            rgba(128, 242, 13, 1.0).greyscale(),
788            rgba(128, 128, 128, 1.0)
789        );
790        assert_approximately_eq!(hsl(90, 90, 50).greyscale(), hsl(90, 0, 50));
791        assert_approximately_eq!(hsla(90, 90, 50, 1.0).greyscale(), hsla(90, 0, 50, 1.0));
792    }
793
794    #[test]
795    fn can_clone() {
796        let rgb_color = rgb(5, 10, 15);
797        let rgba_color = rgba(5, 10, 15, 1.0);
798        let hsl_color = hsl(6, 93, 71);
799        let hsla_color = hsla(6, 93, 71, 1.0);
800
801        assert_eq!(rgb_color, rgb_color.clone());
802        assert_eq!(rgba_color, rgba_color.clone());
803        assert_eq!(hsl_color, hsl_color.clone());
804        assert_eq!(hsla_color, hsla_color.clone());
805    }
806
807    #[test]
808    fn can_copy() {
809        let rgb_color = rgb(172, 95, 82);
810        let rgba_color = rgba(172, 95, 82, 1.0);
811        let hsl_color = hsl(9, 35, 50);
812        let hsla_color = hsla(9, 35, 50, 1.0);
813
814        let copied_rgb_color = rgb_color;
815        let copied_rgba_color = rgba_color;
816        let copied_hsl_color = hsl_color;
817        let copied_hsla_color = hsla_color;
818
819        assert_eq!(rgb_color, copied_rgb_color);
820        assert_eq!(rgba_color, copied_rgba_color);
821        assert_eq!(hsl_color, copied_hsl_color);
822        assert_eq!(hsla_color, copied_hsla_color);
823    }
824
825    #[test]
826    fn can_debug() {
827        let rgb_value = format!("{:?}", rgb(5, 10, 15));
828        let rgba_value = format!("{:?}", rgba(5, 10, 15, 1.0));
829        let hsl_value = format!("{:?}", hsl(6, 93, 71));
830        let hsla_value = format!("{:?}", hsla(6, 93, 71, 1.0));
831
832        assert_eq!(rgb_value, "RGB { r: Ratio(5), g: Ratio(10), b: Ratio(15) }");
833        assert_eq!(
834            rgba_value,
835            "RGBA { r: Ratio(5), g: Ratio(10), b: Ratio(15), a: Ratio(255) }"
836        );
837        assert_eq!(
838            hsl_value,
839            "HSL { h: Angle { degrees: 6 }, s: Ratio(237), l: Ratio(181) }"
840        );
841        assert_eq!(
842            hsla_value,
843            "HSLA { h: Angle { degrees: 6 }, s: Ratio(237), l: Ratio(181), a: Ratio(255) }"
844        );
845    }
846
847    #[test]
848    fn can_convert_to_css() {
849        let rgb = rgb(5, 10, 255);
850        let rgba = rgba(5, 10, 255, 1.0);
851        let hsl = hsl(6, 93, 71);
852        let hsla = hsla(6, 93, 71, 1.0);
853
854        assert_eq!(rgb.to_css(), "rgb(5, 10, 255)");
855        assert_eq!(rgba.to_css(), "rgba(5, 10, 255, 1.00)");
856        assert_eq!(hsl.to_css(), "hsl(6, 93%, 71%)");
857        assert_eq!(hsla.to_css(), "hsla(6, 93%, 71%, 1.00)");
858    }
859
860    #[test]
861    fn can_print_in_css() {
862        let printed_rgb = format!("{}", rgb(5, 10, 255));
863        let printed_rgba = format!("{}", rgba(5, 10, 255, 1.0));
864        let printed_hsl = format!("{}", hsl(6, 93, 71));
865        let printed_hsla = format!("{}", hsla(6, 93, 71, 1.0));
866
867        assert_eq!(printed_rgb, "rgb(5, 10, 255)");
868        assert_eq!(printed_rgba, "rgba(5, 10, 255, 1.00)");
869        assert_eq!(printed_hsl, "hsl(6, 93%, 71%)");
870        assert_eq!(printed_hsla, "hsla(6, 93%, 71%, 1.00)");
871    }
872
873    #[test]
874    fn can_be_displayed() {
875        let rgb = rgb(5, 10, 255);
876        let rgba = rgba(5, 10, 255, 0.75);
877        let hsl = hsl(6, 93, 71);
878        let hsla = hsla(6, 93, 71, 1.0);
879
880        assert_eq!("rgb(5, 10, 255)".to_owned(), format!("{}", rgb));
881        assert_eq!("rgba(5, 10, 255, 0.75)".to_owned(), format!("{}", rgba));
882        assert_eq!("hsl(6, 93%, 71%)".to_owned(), format!("{}", hsl));
883        assert_eq!("hsla(6, 93%, 71%, 1.00)".to_owned(), format!("{}", hsla));
884    }
885
886    #[test]
887    fn can_be_stringified() {
888        let rgb = rgb(5, 10, 255);
889        let rgba = rgba(5, 10, 255, 0.5);
890        let hsl = hsl(6, 93, 71);
891        let hsla = hsla(6, 93, 71, 0.5);
892
893        assert_eq!(String::from("rgb(5, 10, 255)"), rgb.to_string());
894        assert_eq!(String::from("rgba(5, 10, 255, 0.50)"), rgba.to_string());
895        assert_eq!(String::from("hsl(6, 93%, 71%)"), hsl.to_string());
896        assert_eq!(String::from("hsla(6, 93%, 71%, 0.50)"), hsla.to_string());
897    }
898}