Skip to main content

native_theme/model/
bundled.rs

1// Feature-gated bundled SVG icon access via include_bytes!
2//
3// With `material-icons` or `lucide-icons` features enabled, this module
4// embeds SVG files at compile time and makes them available via
5// `bundled_icon_svg()`. Without features, the function always returns None.
6
7use super::icons::{IconRole, IconSet};
8
9/// Returns the raw SVG bytes for a bundled icon, if the corresponding
10/// feature flag is enabled.
11///
12/// Returns `None` when:
13/// - The requested `IconSet` is not `Material` or `Lucide`
14/// - The feature flag for the requested set is not enabled
15///
16/// Returns `Some(&[u8])` containing valid SVG bytes when the feature
17/// is enabled. Callers typically convert to `IconData::Svg(bytes.to_vec())`.
18///
19/// # Examples
20///
21/// ```
22/// use native_theme::{IconSet, IconRole, bundled_icon_svg};
23///
24/// // Without features enabled, bundled icons return None
25/// let result = bundled_icon_svg(IconRole::ActionCopy, IconSet::SfSymbols);
26/// assert!(result.is_none());
27/// ```
28#[must_use = "this returns SVG bytes; it does not render the icon"]
29#[allow(unreachable_patterns, unused_variables)]
30pub fn bundled_icon_svg(role: IconRole, set: IconSet) -> Option<&'static [u8]> {
31    match set {
32        #[cfg(feature = "material-icons")]
33        IconSet::Material => material_svg(role),
34
35        #[cfg(feature = "lucide-icons")]
36        IconSet::Lucide => lucide_svg(role),
37
38        _ => None,
39    }
40}
41
42#[cfg(feature = "material-icons")]
43#[allow(unreachable_patterns)]
44fn material_svg(role: IconRole) -> Option<&'static [u8]> {
45    match role {
46        // Dialog / Alert (6)
47        IconRole::DialogWarning => Some(include_bytes!("../../icons/material/warning.svg")),
48        IconRole::DialogError => Some(include_bytes!("../../icons/material/error.svg")),
49        IconRole::DialogInfo => Some(include_bytes!("../../icons/material/info.svg")),
50        IconRole::DialogQuestion => Some(include_bytes!("../../icons/material/help.svg")),
51        IconRole::DialogSuccess => Some(include_bytes!("../../icons/material/check_circle.svg")),
52        IconRole::Shield => Some(include_bytes!("../../icons/material/shield.svg")),
53
54        // Window Controls (4)
55        IconRole::WindowClose => Some(include_bytes!("../../icons/material/close.svg")),
56        IconRole::WindowMinimize => Some(include_bytes!("../../icons/material/minimize.svg")),
57        IconRole::WindowMaximize => Some(include_bytes!("../../icons/material/open_in_full.svg")),
58        IconRole::WindowRestore => {
59            Some(include_bytes!("../../icons/material/close_fullscreen.svg"))
60        }
61
62        // Common Actions (14)
63        IconRole::ActionSave => Some(include_bytes!("../../icons/material/save.svg")),
64        IconRole::ActionDelete => Some(include_bytes!("../../icons/material/delete.svg")),
65        IconRole::ActionCopy => Some(include_bytes!("../../icons/material/content_copy.svg")),
66        IconRole::ActionPaste => Some(include_bytes!("../../icons/material/content_paste.svg")),
67        IconRole::ActionCut => Some(include_bytes!("../../icons/material/content_cut.svg")),
68        IconRole::ActionUndo => Some(include_bytes!("../../icons/material/undo.svg")),
69        IconRole::ActionRedo => Some(include_bytes!("../../icons/material/redo.svg")),
70        IconRole::ActionSearch => Some(include_bytes!("../../icons/material/search.svg")),
71        IconRole::ActionSettings => Some(include_bytes!("../../icons/material/settings.svg")),
72        IconRole::ActionEdit => Some(include_bytes!("../../icons/material/edit.svg")),
73        IconRole::ActionAdd => Some(include_bytes!("../../icons/material/add.svg")),
74        IconRole::ActionRemove => Some(include_bytes!("../../icons/material/remove.svg")),
75        IconRole::ActionRefresh => Some(include_bytes!("../../icons/material/refresh.svg")),
76        IconRole::ActionPrint => Some(include_bytes!("../../icons/material/print.svg")),
77
78        // Navigation (6)
79        IconRole::NavBack => Some(include_bytes!("../../icons/material/arrow_back.svg")),
80        IconRole::NavForward => Some(include_bytes!("../../icons/material/arrow_forward.svg")),
81        IconRole::NavUp => Some(include_bytes!("../../icons/material/arrow_upward.svg")),
82        IconRole::NavDown => Some(include_bytes!("../../icons/material/arrow_downward.svg")),
83        IconRole::NavHome => Some(include_bytes!("../../icons/material/home.svg")),
84        IconRole::NavMenu => Some(include_bytes!("../../icons/material/menu.svg")),
85
86        // Files / Places (5)
87        IconRole::FileGeneric => Some(include_bytes!("../../icons/material/description.svg")),
88        IconRole::FolderClosed => Some(include_bytes!("../../icons/material/folder.svg")),
89        IconRole::FolderOpen => Some(include_bytes!("../../icons/material/folder_open.svg")),
90        IconRole::TrashEmpty => Some(include_bytes!("../../icons/material/delete.svg")),
91        IconRole::TrashFull => Some(include_bytes!("../../icons/material/delete.svg")), // reuse delete
92
93        // Status (3)
94        IconRole::StatusBusy => Some(include_bytes!("../../icons/material/progress_activity.svg")),
95        IconRole::StatusCheck => Some(include_bytes!("../../icons/material/check.svg")),
96        IconRole::StatusError => Some(include_bytes!("../../icons/material/error.svg")), // reuse error
97
98        // System (4)
99        IconRole::UserAccount => Some(include_bytes!("../../icons/material/person.svg")),
100        IconRole::Notification => Some(include_bytes!("../../icons/material/notifications.svg")),
101        IconRole::Help => Some(include_bytes!("../../icons/material/help.svg")), // reuse help
102        IconRole::Lock => Some(include_bytes!("../../icons/material/lock.svg")),
103
104        _ => None, // #[non_exhaustive] forward compat
105    }
106}
107
108#[cfg(feature = "lucide-icons")]
109#[allow(unreachable_patterns)]
110fn lucide_svg(role: IconRole) -> Option<&'static [u8]> {
111    match role {
112        // Dialog / Alert (6)
113        IconRole::DialogWarning => Some(include_bytes!("../../icons/lucide/triangle-alert.svg")),
114        IconRole::DialogError => Some(include_bytes!("../../icons/lucide/circle-x.svg")),
115        IconRole::DialogInfo => Some(include_bytes!("../../icons/lucide/info.svg")),
116        IconRole::DialogQuestion => Some(include_bytes!(
117            "../../icons/lucide/circle-question-mark.svg"
118        )),
119        IconRole::DialogSuccess => Some(include_bytes!("../../icons/lucide/circle-check.svg")),
120        IconRole::Shield => Some(include_bytes!("../../icons/lucide/shield.svg")),
121
122        // Window Controls (4)
123        IconRole::WindowClose => Some(include_bytes!("../../icons/lucide/x.svg")),
124        IconRole::WindowMinimize => Some(include_bytes!("../../icons/lucide/minimize.svg")),
125        IconRole::WindowMaximize => Some(include_bytes!("../../icons/lucide/maximize.svg")),
126        IconRole::WindowRestore => Some(include_bytes!("../../icons/lucide/minimize-2.svg")),
127
128        // Common Actions (14)
129        IconRole::ActionSave => Some(include_bytes!("../../icons/lucide/save.svg")),
130        IconRole::ActionDelete => Some(include_bytes!("../../icons/lucide/trash-2.svg")),
131        IconRole::ActionCopy => Some(include_bytes!("../../icons/lucide/copy.svg")),
132        IconRole::ActionPaste => Some(include_bytes!("../../icons/lucide/clipboard-paste.svg")),
133        IconRole::ActionCut => Some(include_bytes!("../../icons/lucide/scissors.svg")),
134        IconRole::ActionUndo => Some(include_bytes!("../../icons/lucide/undo-2.svg")),
135        IconRole::ActionRedo => Some(include_bytes!("../../icons/lucide/redo-2.svg")),
136        IconRole::ActionSearch => Some(include_bytes!("../../icons/lucide/search.svg")),
137        IconRole::ActionSettings => Some(include_bytes!("../../icons/lucide/settings.svg")),
138        IconRole::ActionEdit => Some(include_bytes!("../../icons/lucide/pencil.svg")),
139        IconRole::ActionAdd => Some(include_bytes!("../../icons/lucide/plus.svg")),
140        IconRole::ActionRemove => Some(include_bytes!("../../icons/lucide/minus.svg")),
141        IconRole::ActionRefresh => Some(include_bytes!("../../icons/lucide/refresh-cw.svg")),
142        IconRole::ActionPrint => Some(include_bytes!("../../icons/lucide/printer.svg")),
143
144        // Navigation (6)
145        IconRole::NavBack => Some(include_bytes!("../../icons/lucide/chevron-left.svg")),
146        IconRole::NavForward => Some(include_bytes!("../../icons/lucide/chevron-right.svg")),
147        IconRole::NavUp => Some(include_bytes!("../../icons/lucide/chevron-up.svg")),
148        IconRole::NavDown => Some(include_bytes!("../../icons/lucide/chevron-down.svg")),
149        IconRole::NavHome => Some(include_bytes!("../../icons/lucide/house.svg")),
150        IconRole::NavMenu => Some(include_bytes!("../../icons/lucide/menu.svg")),
151
152        // Files / Places (5)
153        IconRole::FileGeneric => Some(include_bytes!("../../icons/lucide/file.svg")),
154        IconRole::FolderClosed => Some(include_bytes!("../../icons/lucide/folder-closed.svg")),
155        IconRole::FolderOpen => Some(include_bytes!("../../icons/lucide/folder-open.svg")),
156        IconRole::TrashEmpty => Some(include_bytes!("../../icons/lucide/trash-2.svg")),
157        IconRole::TrashFull => Some(include_bytes!("../../icons/lucide/trash-2.svg")), // reuse trash-2
158
159        // Status (3)
160        IconRole::StatusBusy => Some(include_bytes!("../../icons/lucide/loader.svg")),
161        IconRole::StatusCheck => Some(include_bytes!("../../icons/lucide/check.svg")),
162        IconRole::StatusError => Some(include_bytes!("../../icons/lucide/circle-x.svg")), // reuse circle-x
163
164        // System (4)
165        IconRole::UserAccount => Some(include_bytes!("../../icons/lucide/user.svg")),
166        IconRole::Notification => Some(include_bytes!("../../icons/lucide/bell.svg")),
167        IconRole::Help => Some(include_bytes!(
168            "../../icons/lucide/circle-question-mark.svg"
169        )), // reuse
170        IconRole::Lock => Some(include_bytes!("../../icons/lucide/lock.svg")),
171
172        _ => None, // #[non_exhaustive] forward compat
173    }
174}
175
176/// Returns raw SVG bytes for a bundled icon looked up by its canonical name
177/// within the icon set.
178///
179/// Names use each set's canonical format:
180/// - Lucide: kebab-case (e.g., `"arrow-down"`, `"circle-check"`)
181/// - Material: snake_case (e.g., `"arrow_downward"`, `"check_circle"`)
182///
183/// Returns `None` for non-bundled sets, disabled features, or unknown names.
184///
185/// # Examples
186///
187/// ```
188/// use native_theme::{IconSet, bundled_icon_by_name};
189///
190/// // Without features enabled, bundled icons return None
191/// let result = bundled_icon_by_name("check", IconSet::SfSymbols);
192/// assert!(result.is_none());
193/// ```
194#[must_use = "this returns SVG bytes; it does not render the icon"]
195#[allow(unreachable_patterns, unused_variables)]
196pub fn bundled_icon_by_name(name: &str, set: IconSet) -> Option<&'static [u8]> {
197    match set {
198        #[cfg(feature = "material-icons")]
199        IconSet::Material => material_svg_by_name(name),
200
201        #[cfg(feature = "lucide-icons")]
202        IconSet::Lucide => lucide_svg_by_name(name),
203
204        _ => None,
205    }
206}
207
208#[cfg(feature = "lucide-icons")]
209fn lucide_svg_by_name(name: &str) -> Option<&'static [u8]> {
210    match name {
211        "a-large-small" => Some(include_bytes!("../../icons/lucide/a-large-small.svg")),
212        "arrow-down" => Some(include_bytes!("../../icons/lucide/arrow-down.svg")),
213        "arrow-left" => Some(include_bytes!("../../icons/lucide/arrow-left.svg")),
214        "arrow-right" => Some(include_bytes!("../../icons/lucide/arrow-right.svg")),
215        "arrow-up" => Some(include_bytes!("../../icons/lucide/arrow-up.svg")),
216        "asterisk" => Some(include_bytes!("../../icons/lucide/asterisk.svg")),
217        "bell" => Some(include_bytes!("../../icons/lucide/bell.svg")),
218        "book-open" => Some(include_bytes!("../../icons/lucide/book-open.svg")),
219        "bot" => Some(include_bytes!("../../icons/lucide/bot.svg")),
220        "building-2" => Some(include_bytes!("../../icons/lucide/building-2.svg")),
221        "calendar" => Some(include_bytes!("../../icons/lucide/calendar.svg")),
222        "case-sensitive" => Some(include_bytes!("../../icons/lucide/case-sensitive.svg")),
223        "chart-pie" => Some(include_bytes!("../../icons/lucide/chart-pie.svg")),
224        "check" => Some(include_bytes!("../../icons/lucide/check.svg")),
225        "chevron-down" => Some(include_bytes!("../../icons/lucide/chevron-down.svg")),
226        "chevron-left" => Some(include_bytes!("../../icons/lucide/chevron-left.svg")),
227        "chevron-right" => Some(include_bytes!("../../icons/lucide/chevron-right.svg")),
228        "chevrons-up-down" => Some(include_bytes!("../../icons/lucide/chevrons-up-down.svg")),
229        "chevron-up" => Some(include_bytes!("../../icons/lucide/chevron-up.svg")),
230        "circle-check" => Some(include_bytes!("../../icons/lucide/circle-check.svg")),
231        "circle-question-mark" => Some(include_bytes!(
232            "../../icons/lucide/circle-question-mark.svg"
233        )),
234        "circle-user" => Some(include_bytes!("../../icons/lucide/circle-user.svg")),
235        "circle-x" => Some(include_bytes!("../../icons/lucide/circle-x.svg")),
236        "clipboard-paste" => Some(include_bytes!("../../icons/lucide/clipboard-paste.svg")),
237        "close" => Some(include_bytes!("../../icons/lucide/close.svg")),
238        "copy" => Some(include_bytes!("../../icons/lucide/copy.svg")),
239        "dash" => Some(include_bytes!("../../icons/lucide/dash.svg")),
240        "delete" => Some(include_bytes!("../../icons/lucide/delete.svg")),
241        "ellipsis" => Some(include_bytes!("../../icons/lucide/ellipsis.svg")),
242        "ellipsis-vertical" => Some(include_bytes!("../../icons/lucide/ellipsis-vertical.svg")),
243        "external-link" => Some(include_bytes!("../../icons/lucide/external-link.svg")),
244        "eye" => Some(include_bytes!("../../icons/lucide/eye.svg")),
245        "eye-off" => Some(include_bytes!("../../icons/lucide/eye-off.svg")),
246        "file" => Some(include_bytes!("../../icons/lucide/file.svg")),
247        "folder" => Some(include_bytes!("../../icons/lucide/folder.svg")),
248        "folder-closed" => Some(include_bytes!("../../icons/lucide/folder-closed.svg")),
249        "folder-open" => Some(include_bytes!("../../icons/lucide/folder-open.svg")),
250        "frame" => Some(include_bytes!("../../icons/lucide/frame.svg")),
251        "gallery-vertical-end" => Some(include_bytes!(
252            "../../icons/lucide/gallery-vertical-end.svg"
253        )),
254        "github" => Some(include_bytes!("../../icons/lucide/github.svg")),
255        "globe" => Some(include_bytes!("../../icons/lucide/globe.svg")),
256        "heart" => Some(include_bytes!("../../icons/lucide/heart.svg")),
257        "heart-off" => Some(include_bytes!("../../icons/lucide/heart-off.svg")),
258        "house" => Some(include_bytes!("../../icons/lucide/house.svg")),
259        "inbox" => Some(include_bytes!("../../icons/lucide/inbox.svg")),
260        "info" => Some(include_bytes!("../../icons/lucide/info.svg")),
261        "inspect" => Some(include_bytes!("../../icons/lucide/inspect.svg")),
262        "layout-dashboard" => Some(include_bytes!("../../icons/lucide/layout-dashboard.svg")),
263        "loader" => Some(include_bytes!("../../icons/lucide/loader.svg")),
264        "loader-circle" => Some(include_bytes!("../../icons/lucide/loader-circle.svg")),
265        "lock" => Some(include_bytes!("../../icons/lucide/lock.svg")),
266        "map" => Some(include_bytes!("../../icons/lucide/map.svg")),
267        "maximize" => Some(include_bytes!("../../icons/lucide/maximize.svg")),
268        "menu" => Some(include_bytes!("../../icons/lucide/menu.svg")),
269        "minimize" => Some(include_bytes!("../../icons/lucide/minimize.svg")),
270        "minimize-2" => Some(include_bytes!("../../icons/lucide/minimize-2.svg")),
271        "minus" => Some(include_bytes!("../../icons/lucide/minus.svg")),
272        "moon" => Some(include_bytes!("../../icons/lucide/moon.svg")),
273        "palette" => Some(include_bytes!("../../icons/lucide/palette.svg")),
274        "panel-bottom" => Some(include_bytes!("../../icons/lucide/panel-bottom.svg")),
275        "panel-bottom-open" => Some(include_bytes!("../../icons/lucide/panel-bottom-open.svg")),
276        "panel-left" => Some(include_bytes!("../../icons/lucide/panel-left.svg")),
277        "panel-left-close" => Some(include_bytes!("../../icons/lucide/panel-left-close.svg")),
278        "panel-left-open" => Some(include_bytes!("../../icons/lucide/panel-left-open.svg")),
279        "panel-right" => Some(include_bytes!("../../icons/lucide/panel-right.svg")),
280        "panel-right-close" => Some(include_bytes!("../../icons/lucide/panel-right-close.svg")),
281        "panel-right-open" => Some(include_bytes!("../../icons/lucide/panel-right-open.svg")),
282        "pencil" => Some(include_bytes!("../../icons/lucide/pencil.svg")),
283        "plus" => Some(include_bytes!("../../icons/lucide/plus.svg")),
284        "printer" => Some(include_bytes!("../../icons/lucide/printer.svg")),
285        "redo" => Some(include_bytes!("../../icons/lucide/redo.svg")),
286        "redo-2" => Some(include_bytes!("../../icons/lucide/redo-2.svg")),
287        "refresh-cw" => Some(include_bytes!("../../icons/lucide/refresh-cw.svg")),
288        "replace" => Some(include_bytes!("../../icons/lucide/replace.svg")),
289        "resize-corner" => Some(include_bytes!("../../icons/lucide/resize-corner.svg")),
290        "save" => Some(include_bytes!("../../icons/lucide/save.svg")),
291        "scissors" => Some(include_bytes!("../../icons/lucide/scissors.svg")),
292        "search" => Some(include_bytes!("../../icons/lucide/search.svg")),
293        "settings" => Some(include_bytes!("../../icons/lucide/settings.svg")),
294        "settings-2" => Some(include_bytes!("../../icons/lucide/settings-2.svg")),
295        "shield" => Some(include_bytes!("../../icons/lucide/shield.svg")),
296        "sort-ascending" => Some(include_bytes!("../../icons/lucide/sort-ascending.svg")),
297        "sort-descending" => Some(include_bytes!("../../icons/lucide/sort-descending.svg")),
298        "square-terminal" => Some(include_bytes!("../../icons/lucide/square-terminal.svg")),
299        "star" => Some(include_bytes!("../../icons/lucide/star.svg")),
300        "star-off" => Some(include_bytes!("../../icons/lucide/star-off.svg")),
301        "sun" => Some(include_bytes!("../../icons/lucide/sun.svg")),
302        "thumbs-down" => Some(include_bytes!("../../icons/lucide/thumbs-down.svg")),
303        "thumbs-up" => Some(include_bytes!("../../icons/lucide/thumbs-up.svg")),
304        "trash-2" => Some(include_bytes!("../../icons/lucide/trash-2.svg")),
305        "triangle-alert" => Some(include_bytes!("../../icons/lucide/triangle-alert.svg")),
306        "undo" => Some(include_bytes!("../../icons/lucide/undo.svg")),
307        "undo-2" => Some(include_bytes!("../../icons/lucide/undo-2.svg")),
308        "user" => Some(include_bytes!("../../icons/lucide/user.svg")),
309        "window-close" => Some(include_bytes!("../../icons/lucide/window-close.svg")),
310        "window-maximize" => Some(include_bytes!("../../icons/lucide/window-maximize.svg")),
311        "window-minimize" => Some(include_bytes!("../../icons/lucide/window-minimize.svg")),
312        "window-restore" => Some(include_bytes!("../../icons/lucide/window-restore.svg")),
313        "x" => Some(include_bytes!("../../icons/lucide/x.svg")),
314        _ => None,
315    }
316}
317
318#[cfg(feature = "material-icons")]
319fn material_svg_by_name(name: &str) -> Option<&'static [u8]> {
320    match name {
321        "account_circle" => Some(include_bytes!("../../icons/material/account_circle.svg")),
322        "add" => Some(include_bytes!("../../icons/material/add.svg")),
323        "apartment" => Some(include_bytes!("../../icons/material/apartment.svg")),
324        "arrow_back" => Some(include_bytes!("../../icons/material/arrow_back.svg")),
325        "arrow_downward" => Some(include_bytes!("../../icons/material/arrow_downward.svg")),
326        "arrow_forward" => Some(include_bytes!("../../icons/material/arrow_forward.svg")),
327        "arrow_upward" => Some(include_bytes!("../../icons/material/arrow_upward.svg")),
328        "autorenew" => Some(include_bytes!("../../icons/material/autorenew.svg")),
329        "calendar_today" => Some(include_bytes!("../../icons/material/calendar_today.svg")),
330        "cancel" => Some(include_bytes!("../../icons/material/cancel.svg")),
331        "check" => Some(include_bytes!("../../icons/material/check.svg")),
332        "check_circle" => Some(include_bytes!("../../icons/material/check_circle.svg")),
333        "chevron_left" => Some(include_bytes!("../../icons/material/chevron_left.svg")),
334        "chevron_right" => Some(include_bytes!("../../icons/material/chevron_right.svg")),
335        "close" => Some(include_bytes!("../../icons/material/close.svg")),
336        "close_fullscreen" => Some(include_bytes!("../../icons/material/close_fullscreen.svg")),
337        "code" => Some(include_bytes!("../../icons/material/code.svg")),
338        "content_copy" => Some(include_bytes!("../../icons/material/content_copy.svg")),
339        "crop_free" => Some(include_bytes!("../../icons/material/crop_free.svg")),
340        "dark_mode" => Some(include_bytes!("../../icons/material/dark_mode.svg")),
341        "dashboard" => Some(include_bytes!("../../icons/material/dashboard.svg")),
342        "delete" => Some(include_bytes!("../../icons/material/delete.svg")),
343        "description" => Some(include_bytes!("../../icons/material/description.svg")),
344        "developer_mode" => Some(include_bytes!("../../icons/material/developer_mode.svg")),
345        "dock_to_bottom" => Some(include_bytes!("../../icons/material/dock_to_bottom.svg")),
346        "drag_indicator" => Some(include_bytes!("../../icons/material/drag_indicator.svg")),
347        "emergency" => Some(include_bytes!("../../icons/material/emergency.svg")),
348        "expand_less" => Some(include_bytes!("../../icons/material/expand_less.svg")),
349        "expand_more" => Some(include_bytes!("../../icons/material/expand_more.svg")),
350        "favorite" => Some(include_bytes!("../../icons/material/favorite.svg")),
351        "find_replace" => Some(include_bytes!("../../icons/material/find_replace.svg")),
352        "folder" => Some(include_bytes!("../../icons/material/folder.svg")),
353        "folder_open" => Some(include_bytes!("../../icons/material/folder_open.svg")),
354        "font_size" => Some(include_bytes!("../../icons/material/font_size.svg")),
355        "heart_broken" => Some(include_bytes!("../../icons/material/heart_broken.svg")),
356        "inbox" => Some(include_bytes!("../../icons/material/inbox.svg")),
357        "info" => Some(include_bytes!("../../icons/material/info.svg")),
358        "language" => Some(include_bytes!("../../icons/material/language.svg")),
359        "left_panel_close" => Some(include_bytes!("../../icons/material/left_panel_close.svg")),
360        "left_panel_open" => Some(include_bytes!("../../icons/material/left_panel_open.svg")),
361        "light_mode" => Some(include_bytes!("../../icons/material/light_mode.svg")),
362        "map" => Some(include_bytes!("../../icons/material/map.svg")),
363        "match_case" => Some(include_bytes!("../../icons/material/match_case.svg")),
364        "menu" => Some(include_bytes!("../../icons/material/menu.svg")),
365        "menu_book" => Some(include_bytes!("../../icons/material/menu_book.svg")),
366        "minimize" => Some(include_bytes!("../../icons/material/minimize.svg")),
367        "more_horiz" => Some(include_bytes!("../../icons/material/more_horiz.svg")),
368        "more_vert" => Some(include_bytes!("../../icons/material/more_vert.svg")),
369        "notifications" => Some(include_bytes!("../../icons/material/notifications.svg")),
370        "open_in_full" => Some(include_bytes!("../../icons/material/open_in_full.svg")),
371        "open_in_new" => Some(include_bytes!("../../icons/material/open_in_new.svg")),
372        "palette" => Some(include_bytes!("../../icons/material/palette.svg")),
373        "person" => Some(include_bytes!("../../icons/material/person.svg")),
374        "pie_chart" => Some(include_bytes!("../../icons/material/pie_chart.svg")),
375        "progress_activity" => Some(include_bytes!("../../icons/material/progress_activity.svg")),
376        "redo" => Some(include_bytes!("../../icons/material/redo.svg")),
377        "remove" => Some(include_bytes!("../../icons/material/remove.svg")),
378        "right_panel_close" => Some(include_bytes!("../../icons/material/right_panel_close.svg")),
379        "right_panel_open" => Some(include_bytes!("../../icons/material/right_panel_open.svg")),
380        "search" => Some(include_bytes!("../../icons/material/search.svg")),
381        "settings" => Some(include_bytes!("../../icons/material/settings.svg")),
382        "side_navigation" => Some(include_bytes!("../../icons/material/side_navigation.svg")),
383        "smart_toy" => Some(include_bytes!("../../icons/material/smart_toy.svg")),
384        "star" => Some(include_bytes!("../../icons/material/star.svg")),
385        "star_border" => Some(include_bytes!("../../icons/material/star_border.svg")),
386        "terminal" => Some(include_bytes!("../../icons/material/terminal.svg")),
387        "thumb_down" => Some(include_bytes!("../../icons/material/thumb_down.svg")),
388        "thumb_up" => Some(include_bytes!("../../icons/material/thumb_up.svg")),
389        "tune" => Some(include_bytes!("../../icons/material/tune.svg")),
390        "undo" => Some(include_bytes!("../../icons/material/undo.svg")),
391        "unfold_more" => Some(include_bytes!("../../icons/material/unfold_more.svg")),
392        "view_carousel" => Some(include_bytes!("../../icons/material/view_carousel.svg")),
393        "visibility" => Some(include_bytes!("../../icons/material/visibility.svg")),
394        "visibility_off" => Some(include_bytes!("../../icons/material/visibility_off.svg")),
395        "warning" => Some(include_bytes!("../../icons/material/warning.svg")),
396        "web_asset" => Some(include_bytes!("../../icons/material/web_asset.svg")),
397        _ => None,
398    }
399}
400
401#[cfg(test)]
402#[allow(clippy::unwrap_used, clippy::expect_used)]
403mod tests {
404    use super::*;
405
406    // === Feature-gated tests: Material ===
407
408    #[test]
409    #[cfg(feature = "material-icons")]
410    fn material_icons_cover_all_roles() {
411        for role in IconRole::ALL {
412            let svg = bundled_icon_svg(role, IconSet::Material);
413            assert!(svg.is_some(), "Material icons missing SVG for {:?}", role);
414            let bytes = svg.unwrap();
415            let content = std::str::from_utf8(bytes).expect("SVG should be valid UTF-8");
416            assert!(
417                content.contains("<svg"),
418                "Material {:?} does not contain <svg tag",
419                role
420            );
421        }
422    }
423
424    #[test]
425    #[cfg(feature = "material-icons")]
426    fn material_icons_total_size_under_200kb() {
427        let total: usize = IconRole::ALL
428            .iter()
429            .filter_map(|role| bundled_icon_svg(*role, IconSet::Material))
430            .map(|svg| svg.len())
431            .sum();
432        assert!(
433            total < 400 * 1024,
434            "Material icons total size {} bytes exceeds 400KB budget",
435            total
436        );
437    }
438
439    // === Feature-gated tests: Lucide ===
440
441    #[test]
442    #[cfg(feature = "lucide-icons")]
443    fn lucide_icons_cover_all_roles() {
444        for role in IconRole::ALL {
445            let svg = bundled_icon_svg(role, IconSet::Lucide);
446            assert!(svg.is_some(), "Lucide icons missing SVG for {:?}", role);
447            let bytes = svg.unwrap();
448            let content = std::str::from_utf8(bytes).expect("SVG should be valid UTF-8");
449            assert!(
450                content.contains("<svg"),
451                "Lucide {:?} does not contain <svg tag",
452                role
453            );
454        }
455    }
456
457    #[test]
458    #[cfg(feature = "lucide-icons")]
459    fn lucide_icons_total_size_under_100kb() {
460        let total: usize = IconRole::ALL
461            .iter()
462            .filter_map(|role| bundled_icon_svg(*role, IconSet::Lucide))
463            .map(|svg| svg.len())
464            .sum();
465        assert!(
466            total < 200 * 1024,
467            "Lucide icons total size {} bytes exceeds 200KB budget",
468            total
469        );
470    }
471
472    // === Non-feature-gated tests ===
473
474    #[test]
475    fn non_bundled_sets_return_none() {
476        assert!(
477            bundled_icon_svg(IconRole::ActionCopy, IconSet::SfSymbols).is_none(),
478            "SfSymbols should not be a bundled set"
479        );
480        assert!(
481            bundled_icon_svg(IconRole::ActionCopy, IconSet::Freedesktop).is_none(),
482            "Freedesktop should not be a bundled set"
483        );
484        assert!(
485            bundled_icon_svg(IconRole::ActionCopy, IconSet::SegoeIcons).is_none(),
486            "SegoeIcons should not be a bundled set"
487        );
488    }
489
490    // === bundled_icon_by_name tests ===
491
492    #[test]
493    #[cfg(feature = "lucide-icons")]
494    fn lucide_by_name_covers_gpui_icons() {
495        let names = [
496            "a-large-small",
497            "arrow-down",
498            "arrow-left",
499            "arrow-right",
500            "arrow-up",
501            "asterisk",
502            "bell",
503            "book-open",
504            "bot",
505            "building-2",
506            "calendar",
507            "case-sensitive",
508            "chart-pie",
509            "check",
510            "chevron-down",
511            "chevron-left",
512            "chevron-right",
513            "chevrons-up-down",
514            "chevron-up",
515            "circle-check",
516            "circle-user",
517            "circle-x",
518            "close",
519            "copy",
520            "dash",
521            "delete",
522            "ellipsis",
523            "ellipsis-vertical",
524            "external-link",
525            "eye",
526            "eye-off",
527            "file",
528            "folder",
529            "folder-closed",
530            "folder-open",
531            "frame",
532            "gallery-vertical-end",
533            "github",
534            "globe",
535            "heart",
536            "heart-off",
537            "inbox",
538            "info",
539            "inspect",
540            "layout-dashboard",
541            "loader",
542            "loader-circle",
543            "map",
544            "maximize",
545            "menu",
546            "minimize",
547            "minus",
548            "moon",
549            "palette",
550            "panel-bottom",
551            "panel-bottom-open",
552            "panel-left",
553            "panel-left-close",
554            "panel-left-open",
555            "panel-right",
556            "panel-right-close",
557            "panel-right-open",
558            "plus",
559            "redo",
560            "redo-2",
561            "replace",
562            "resize-corner",
563            "search",
564            "settings",
565            "settings-2",
566            "sort-ascending",
567            "sort-descending",
568            "square-terminal",
569            "star",
570            "star-off",
571            "sun",
572            "thumbs-down",
573            "thumbs-up",
574            "triangle-alert",
575            "undo",
576            "undo-2",
577            "user",
578            "window-close",
579            "window-maximize",
580            "window-minimize",
581            "window-restore",
582        ];
583        for name in names {
584            let svg = bundled_icon_by_name(name, IconSet::Lucide);
585            assert!(svg.is_some(), "Lucide by-name missing: {}", name);
586            let bytes = svg.unwrap();
587            let content = std::str::from_utf8(bytes).expect("SVG should be valid UTF-8");
588            assert!(
589                content.contains("<svg"),
590                "Lucide {} does not contain <svg tag",
591                name
592            );
593        }
594    }
595
596    #[test]
597    #[cfg(feature = "material-icons")]
598    fn material_by_name_covers_gpui_icons() {
599        let names = [
600            "font_size",
601            "arrow_downward",
602            "arrow_back",
603            "arrow_forward",
604            "arrow_upward",
605            "emergency",
606            "notifications",
607            "menu_book",
608            "smart_toy",
609            "apartment",
610            "calendar_today",
611            "match_case",
612            "pie_chart",
613            "check",
614            "expand_more",
615            "chevron_left",
616            "chevron_right",
617            "unfold_more",
618            "expand_less",
619            "check_circle",
620            "account_circle",
621            "cancel",
622            "close",
623            "content_copy",
624            "remove",
625            "delete",
626            "more_horiz",
627            "more_vert",
628            "open_in_new",
629            "visibility",
630            "visibility_off",
631            "description",
632            "folder",
633            "folder_open",
634            "crop_free",
635            "view_carousel",
636            "code",
637            "language",
638            "favorite",
639            "heart_broken",
640            "inbox",
641            "info",
642            "developer_mode",
643            "dashboard",
644            "progress_activity",
645            "autorenew",
646            "map",
647            "open_in_full",
648            "menu",
649            "minimize",
650            "dark_mode",
651            "palette",
652            "dock_to_bottom",
653            "web_asset",
654            "side_navigation",
655            "left_panel_close",
656            "left_panel_open",
657            "right_panel_close",
658            "right_panel_open",
659            "add",
660            "redo",
661            "find_replace",
662            "drag_indicator",
663            "search",
664            "settings",
665            "tune",
666            "terminal",
667            "star",
668            "star_border",
669            "light_mode",
670            "thumb_down",
671            "thumb_up",
672            "warning",
673            "undo",
674            "person",
675            "close_fullscreen",
676        ];
677        for name in names {
678            let svg = bundled_icon_by_name(name, IconSet::Material);
679            assert!(svg.is_some(), "Material by-name missing: {}", name);
680            let bytes = svg.unwrap();
681            let content = std::str::from_utf8(bytes).expect("SVG should be valid UTF-8");
682            assert!(
683                content.contains("<svg"),
684                "Material {} does not contain <svg tag",
685                name
686            );
687        }
688    }
689
690    #[test]
691    fn by_name_non_bundled_sets_return_none() {
692        assert!(bundled_icon_by_name("check", IconSet::SfSymbols).is_none());
693        assert!(bundled_icon_by_name("check", IconSet::Freedesktop).is_none());
694        assert!(bundled_icon_by_name("check", IconSet::SegoeIcons).is_none());
695    }
696
697    #[test]
698    fn by_name_unknown_name_returns_none() {
699        assert!(bundled_icon_by_name("nonexistent-icon-xyz", IconSet::Lucide).is_none());
700        assert!(bundled_icon_by_name("nonexistent_icon_xyz", IconSet::Material).is_none());
701    }
702}