palette_derive/convert/
util.rs
1use std::collections::HashMap;
2
3use proc_macro2::Span;
4use syn::{parse_quote, Generics, Result, Type};
5
6use crate::{
7 color_types::{ColorInfo, MetaTypeSource},
8 meta::TypeItemAttributes,
9 util,
10};
11
12pub fn white_point_type(
13 white_point: Option<&Type>,
14 rgb_standard: Option<&Type>,
15 luma_standard: Option<&Type>,
16 internal: bool,
17) -> Option<(Type, WhitePointSource)> {
18 white_point
19 .map(|white_point| (white_point.clone(), WhitePointSource::WhitePoint))
20 .or_else(|| {
21 rgb_standard.map(|rgb_standard| {
22 let rgb_standard_path = util::path(["rgb", "RgbStandard"], internal);
23 let rgb_space_path = util::path(["rgb", "RgbSpace"], internal);
24 (
25 parse_quote!(<<#rgb_standard as #rgb_standard_path>::Space as #rgb_space_path>::WhitePoint),
26 WhitePointSource::RgbStandard,
27 )
28 })
29 })
30 .or_else(|| {
31 luma_standard.map(|luma_standard| {
32 let luma_standard_path = util::path(["luma", "LumaStandard"], internal);
33 (
34 parse_quote!(<#luma_standard as #luma_standard_path>::WhitePoint),
35 WhitePointSource::LumaStandard,
36 )
37 })
38 })
39}
40
41pub fn component_type(component: Option<Type>) -> Type {
42 component.unwrap_or_else(|| parse_quote!(f32))
43}
44
45pub(crate) fn get_convert_color_type(
46 color: &ColorInfo,
47 white_point: &Type,
48 component: &Type,
49 meta: &TypeItemAttributes,
50 generics: &mut Generics,
51) -> syn::Result<(Type, UsedInput)> {
52 let mut used_input = UsedInput::default();
53 let color_type = color.get_type(
54 MetaTypeSource::Generics(generics),
55 component,
56 white_point,
57 &mut used_input,
58 InputUser::Target,
59 meta,
60 )?;
61
62 Ok((color_type, used_input))
63}
64
65pub(crate) fn find_nearest_color<'a>(
66 color: &'a ColorInfo,
67 meta: &TypeItemAttributes,
68) -> Result<&'a ColorInfo> {
69 let mut stack = vec![(color, 0)];
70 let mut found = None;
71 let mut visited = HashMap::new();
72
73 assert!(!meta.skip_derives.is_empty());
75
76 while let Some((color, distance)) = stack.pop() {
77 if meta.skip_derives.contains(color.name) {
78 if let Some((_, found_distance)) = found {
79 if distance < found_distance {
80 found = Some((color, distance));
81 continue;
82 }
83 } else {
84 found = Some((color, distance));
85 continue;
86 }
87 }
88
89 if let Some(&previous_distance) = visited.get(color.name) {
90 if previous_distance <= distance {
91 continue;
92 }
93 }
94
95 visited.insert(color.name, distance);
96
97 for group in &meta.color_groups {
99 for candidate in group.colors {
100 if color.name == candidate.preferred_source {
101 stack.push((&candidate.info, distance + 1));
102 }
103 }
104 }
105
106 for group in &meta.color_groups {
108 for candidate in group.colors {
109 if color.name == candidate.info.name {
110 let preferred = group
111 .find_by_name(candidate.preferred_source)
112 .expect("preferred sources have to exist in the group");
113 stack.push((preferred, distance + 1));
114 }
115 }
116 }
117 }
118
119 if let Some((color, _)) = found {
120 Ok(color)
121 } else {
122 Err(::syn::parse::Error::new(
123 Span::call_site(),
124 format!(
125 "none of the skipped colors can be used for converting from {}",
126 color.name
127 ),
128 ))
129 }
130}
131
132#[derive(PartialEq, Eq, Clone, Copy, Debug)]
133pub enum WhitePointSource {
134 WhitePoint,
135 RgbStandard,
136 LumaStandard,
137 ConcreteType,
138 GeneratedGeneric,
139}
140
141#[derive(Debug, Default)]
142pub struct UsedInput {
143 pub white_point: InputUsage,
144}
145
146#[derive(Debug, Default)]
147pub struct InputUsage {
148 used_by_target: bool,
149 used_by_nearest: bool,
150}
151
152impl InputUsage {
153 pub(crate) fn set_used(&mut self, user: InputUser) {
154 match user {
155 InputUser::Target => self.used_by_target = true,
156 InputUser::Nearest => self.used_by_nearest = true,
157 }
158 }
159
160 pub(crate) fn is_used(&self) -> bool {
161 self.used_by_target || self.used_by_nearest
162 }
163
164 pub(crate) fn is_unconstrained(&self) -> bool {
165 !self.used_by_target && self.used_by_nearest
166 }
167}
168
169#[derive(Clone, Copy)]
170pub enum InputUser {
171 Target,
172 Nearest,
173}