1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
use crate::prelude::*;
use crate::Color;

crate::macros::easy_enum! {direction ltr rtl}
crate::macros::easy_enum! {unicode-bidi normal embed bidi-override isolate isolate-override plaintext}
crate::macros::easy_enum! {white-space normal nowrap pre pre-line pre-wrap}
crate::macros::easy_enum! {writing-mode horizontal-tb vertical-rl vertical-lr}
crate::macros::easy_enum! {hanging-punctuation none first last allow-end force-end}
crate::macros::easy_enum! {hyphens manual none auto}
crate::macros::easy_enum! {text-align left right center justify}
crate::macros::easy_enum! {text-align-last left right center justify start end}
crate::macros::easy_enum! {text-justify auto inter-word inter-character none}
crate::macros::easy_enum! {font-stretch normal ultra-condensed extra-condensed condensed semi-condensed semi-expanded expanded extra-expanded ultra-expanded}
crate::macros::easy_enum! {list-style-type disc armenian circle cjk decimal decimal-leading-zero georgian hebrew hiragana hiragana-iroha katakana katakana-iroha lower-alpha lower-greek lower-latin lower-roman none square upper-alpha upper-greek upper-latin upper-roman}
crate::macros::easy_enum! {list-style-position inside outside}
crate::macros::easy_enum! {list-style-image none [string]}
crate::macros::easy_enum! {break-after auto avoid always all avoid-page page left right recto verso avoid-column column avoid-region region}
crate::macros::easy_enum! {break-before auto avoid always all avoid-page page left right recto verso avoid-column column avoid-region region}
crate::macros::easy_enum! {break-inside auto avoid avoid-page avoid-column avoid-region}
crate::macros::easy_enum! {font-variant normal small-caps}
crate::macros::easy_enum! {word-break normal break-all keep-all}
crate::macros::easy_enum! {word-wrap normal break-word}
crate::macros::easy_enum! {font-style normal italic oblique}
crate::macros::easy_enum! {font-size medium xx-small x-small small large x-large xx-large smaller larger [unit]}
crate::macros::easy_enum! {text-transform none capitalize uppercase lowercase}
crate::macros::easy_enum! {font-kerning auto normal none}
crate::macros::easy_enum! {word-spacing normal [unit]}
crate::macros::easy_enum! {text-overflow clip ellipsis [string]}
crate::macros::easy_enum! {vertical-align baseline sub super top text-top middle bottom text-bottom [unit]}
crate::macros::easy_enum! {line-height normal [float] [unit]}
crate::macros::easy_enum! {letter-spacing normal [unit]}
crate::macros::easy_enum! {tab-size [number]}
crate::macros::easy_enum! {text-decoration-style solid double dotted dashed wavy}
crate::macros::easy_enum! {text-decoration-line none underline overline line-through}
crate::macros::easy_enum! {text-rendering auto optimizeSpeed optimizeLegibility geometricPrecision}
crate::macros::easy_enum! {overflow-wrap normal break-word anywhere}
crate::macros::easy_enum! {font-weight normal bold bolder lighter [number]}
crate::macros::easy_color! {color}
crate::macros::easy_color! {text-decoration-color}
crate::macros::unit_value_macro! {text_indent TextIndent}
crate::macros::unit_value_macro! {outline_offset OutlineOffset}

#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
pub enum FontFamily {
	Initial,
	Inherit,
	Unset,
	Some(Vec<String>),
}

#[rustfmt::skip]
impl std::fmt::Display for FontFamily {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match self {
			Self::Initial => "font-family:initial;".fmt(f),
			Self::Inherit => "font-family:inherit;".fmt(f),
			Self::Unset   => "font-family:unset;".fmt(f),
			Self::Some(fonts) => {
				"font-family:".fmt(f)?;
				if let Some((first, rest)) = fonts.split_first() {
					write!(f, r#""{}""#, first)?;
					for font in rest {
						write!(f, r#","{}""#, font)?;
					}
				}
				";".fmt(f)
			},
		}
	}
}

#[macro_export]
macro_rules! font_family {
	(initial)         => {$crate::Property::FontFamily($crate::FontFamily::Initial)};
	(inherit)         => {$crate::Property::FontFamily($crate::FontFamily::Inherit)};
	(unset)           => {$crate::Property::FontFamily($crate::FontFamily::Unset)};
	($($font:expr),+) => {$crate::Property::FontFamily($crate::FontFamily::Some(vec![$($font.into()),+]))};
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord, SmartDefault)]
pub struct TextShadowEffect {
	#[default(crate::color::BLACK)]
	pub color: Color,
	pub offset_x: Unit,
	pub offset_y: Unit,
	pub blur_radius: Unit,
}

#[rustfmt::skip]
impl std::fmt::Display for TextShadowEffect {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		write!(f, "{} {} {} {}", self.color, self.offset_x, self.offset_y, self.blur_radius)
	}
}

impl crate::AppendProperty for TextShadowEffect {
	fn append_property(self, properties: &mut Vec<crate::Property>) {
		TextShadow::Some(vec![self]).append_property(properties)
	}
}

#[derive(Debug, PartialEq, Eq, Hash, Clone, PartialOrd, Ord)]
pub enum TextShadow {
	Initial,
	Inherit,
	Unset,
	Some(Vec<TextShadowEffect>),
}

#[rustfmt::skip]
impl std::fmt::Display for TextShadow {
	fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
		match self {
			Self::Initial => "text-shadow:initial;".fmt(f),
			Self::Inherit => "text-shadow:inherit;".fmt(f),
			Self::Unset   => "text-shadow:unset;".fmt(f),
			Self::Some(effects) => {
				"text-shadow:".fmt(f)?;
				if let Some((first, rest)) = effects.split_first() {
					write!(f, r#"{}"#, first)?;
					for effect in rest {
						write!(f, r#",{}"#, effect)?;
					}
				}
				";".fmt(f)
			},
		}
	}
}

#[test]
fn font_family_values() {
	assert_eq!(font_family!(initial).to_string(), "font-family:initial;");
	assert_eq!(font_family!(inherit).to_string(), "font-family:inherit;");
	assert_eq!(font_family!(unset).to_string(), "font-family:unset;");
	assert_eq!(font_family!("Helvetica", "Arial", "sans-serif").to_string(), r#"font-family:"Helvetica","Arial","sans-serif";"#);
}

#[test]
fn text_shadow_values() {
	assert_eq!(TextShadow::Initial.to_string(), "text-shadow:initial;");
	assert_eq!(TextShadow::Inherit.to_string(), "text-shadow:inherit;");
	assert_eq!(TextShadow::Unset.to_string(), "text-shadow:unset;");
	assert_eq!(TextShadow::Some(vec![TextShadowEffect::default()]).to_string(), "text-shadow:#000000ff 0 0 0;");
	assert_eq!(TextShadow::Some(vec![TextShadowEffect {
		color: Color::from_hex(0xff_00_00_ff),
		offset_x: unit!(1 px),
		offset_y: unit!(2 px),
		blur_radius: unit!(3 px),
	}]).to_string(), "text-shadow:#ff0000ff 1px 2px 3px;");
	assert_eq!(TextShadow::Some(vec![
		TextShadowEffect {
			color: crate::color::RED,
			offset_x: unit!(1 px),
			offset_y: unit!(2 px),
			blur_radius: unit!(3 px),
		},
		TextShadowEffect {
			color: crate::color::LIME,
			offset_x: unit!(5 px),
			offset_y: unit!(6 px),
			blur_radius: unit!(7 px),
		},
	]).to_string(), "text-shadow:#ff0000ff 1px 2px 3px,#00ff00ff 5px 6px 7px;");
}

// css::font!(
//     font "Roboto" 500 italic normal,
//     size 18 px,
//     spacing 3 px,
//     line-height 1.20,
//     color 0xFF,
//     transform upppercase,
//     decoration double underline,
// )