tailwind_css/systems/builder/mod.rs
1use std::{collections::BTreeSet, fmt::Debug};
2
3use crate::{systems::instruction::TailwindInstruction, *};
4
5pub use self::base62::{Base62, BASE62};
6
7mod base62;
8mod methods;
9mod setter;
10
11///
12#[derive(Debug)]
13pub struct TailwindBuilder {
14 ///
15 pub preflight: PreflightSystem,
16 /// All dynamic color properties
17 ///
18 /// Only determined when packing
19 pub palettes: PaletteSystem,
20 /// All dynamic break points
21 ///
22 /// Only determined when packing
23 pub screens: BreakPointSystem,
24 /// All dynamically registered font properties
25 ///
26 /// Only determined when packing
27 pub fonts: FontSystem,
28 /// All dynamically registered effect properties
29 ///
30 /// Only determined when packing
31 pub effects: EffectSystem,
32 pub(crate) objects: BTreeSet<CssInstance>,
33 pub(crate) bundles: BTreeSet<CssBundle>,
34}
35
36impl TailwindBuilder {
37 /// ## Trace mode
38 ///
39 ///
40 /// # Returns
41 /// **Not all instructions can be inline, if not, it will fall back to trace mode**
42 ///
43 /// - Anonymous style sheets, which can be placed inside `style` tags
44 ///
45 /// ## Example
46 /// - input
47 /// ```html
48 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
49 /// ```
50 /// - output
51 /// ```html
52 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
53 /// <style> {} </style>
54 /// ```
55 #[inline]
56 pub fn trace(&mut self, style: &str, obfuscate: bool) -> Result<String> {
57 let out = try_trace(self, style, obfuscate)?;
58 Ok(out.as_traced())
59 }
60 /// ## Inline mode
61 ///
62 ///
63 /// # Returns
64 /// **Not all instructions can be inline, if not, it will fall back to trace mode**
65 ///
66 /// - Anonymous style sheets, which can be placed inside `style` tags
67 ///
68 /// ## Example
69 /// - input
70 /// ```html
71 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
72 /// ```
73 /// - output
74 /// ```html
75 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
76 /// <style> {} </style>
77 /// ```
78 #[inline]
79 pub fn inline(&mut self, style: &str) -> Result<(String, String)> {
80 let out = try_inline(self, style, CssInlineMode::Inline)?;
81 Ok(out.as_inlined())
82 }
83 /// ## Inline mode
84 ///
85 ///
86 /// # Returns
87 /// **Not all instructions can be inline, if not, it will fall back to trace mode**
88 ///
89 /// - Anonymous style sheets, which can be placed inside `style` tags
90 ///
91 /// ## Example
92 /// - input
93 /// ```html
94 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
95 /// ```
96 /// - output
97 /// ```html
98 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
99 /// <style> {} </style>
100 /// ```
101 #[inline]
102 pub fn scope(&mut self, style: &str) -> Result<(String, String)> {
103 let out = try_inline(self, style, CssInlineMode::Scoped)?;
104 Ok(out.as_scope())
105 }
106 /// ## Inline mode
107 ///
108 ///
109 /// # Returns
110 /// **Not all instructions can be inline, if not, it will fall back to trace mode**
111 ///
112 /// - Anonymous style sheets, which can be placed inside `style` tags
113 ///
114 /// ## Example
115 /// - input
116 /// ```html
117 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
118 /// ```
119 /// - output
120 /// ```html
121 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
122 /// <style> {} </style>
123 /// ```
124 #[inline]
125 pub fn data_key(&mut self, style: &str) -> Result<(String, String)> {
126 let out = try_inline(self, style, CssInlineMode::DataKey)?;
127 Ok(out.as_dataset())
128 }
129 /// ## Inline mode
130 ///
131 ///
132 /// # Returns
133 /// **Not all instructions can be inline, if not, it will fall back to trace mode**
134 ///
135 /// - Anonymous style sheets, which can be placed inside `style` tags
136 ///
137 /// ## Example
138 /// - input
139 /// ```html
140 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
141 /// ```
142 /// - output
143 /// ```html
144 /// <div class="p-auto px-px pt-2 pb-2">Test</div>
145 /// <style> {} </style>
146 /// ```
147 #[inline]
148 pub fn data_value(&mut self, style: &str) -> Result<(String, String)> {
149 let out = try_inline(self, style, CssInlineMode::DataValue)?;
150 Ok(out.as_dataset())
151 }
152 /// Bundle all used stylesheets
153 pub fn bundle(&self) -> Result<String> {
154 let mut out = String::with_capacity(1024 * 10);
155 if !self.preflight.disable {
156 out.push_str(&self.preflight.to_string());
157 }
158 for item in &self.objects {
159 item.write_css(&mut out)?;
160 }
161 for item in &self.bundles {
162 item.write_css(&mut out)?;
163 }
164 Ok(out)
165 }
166}
167
168fn parse_tailwind(input: &str) -> Result<Vec<TailwindInstruction>> {
169 let styles = tailwind_ast::parse_tailwind(input)?;
170 Ok(styles.into_iter().map(TailwindInstruction::from).collect())
171}
172
173fn try_trace(tw: &mut TailwindBuilder, style: &str, obfuscate: bool) -> Result<CssBundle> {
174 let parsed = parse_tailwind(style)?;
175 let mut out = CssBundle::default();
176 for item in parsed {
177 let i = CssInstance::new(&*item.get_instance()?, tw, obfuscate);
178 out.add_trace(&i);
179 tw.objects.insert(i);
180 }
181 Ok(out)
182}
183
184fn try_inline(tw: &mut TailwindBuilder, style: &str, mode: CssInlineMode) -> Result<CssBundle> {
185 let parsed = parse_tailwind(style)?;
186 let mut out = CssBundle::default();
187 for item in parsed {
188 let i = CssInstance::new(&*item.get_instance()?, tw, true);
189 match &i.inlineable {
190 true => out.add_inline(i),
191 false => {
192 out.add_trace(&i);
193 tw.objects.insert(i);
194 },
195 };
196 }
197 out.set_mode(mode);
198 tw.bundles.insert(out.to_owned());
199 Ok(out)
200}