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
//! Asset type classification based on file extension.
/// Classification of asset types based on file extension.
///
/// Used by the `asset!` macro to determine the appropriate return type.
#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
pub enum AssetKind {
/// Font files (.ttf, .otf, .ttc, .otc, .woff, .woff2)
/// Returns: `FontAsset`
Font,
/// Image files (.png, .jpg, .jpeg, .gif, .webp, .avif)
/// Returns: `Photo`
Image,
/// Video files (.mp4, .mov, .webm, .avi)
/// Returns: `Video`
Video,
/// Audio files (.mp3, .wav, .ogg, .flac, .aac)
/// Returns: `Audio`
Audio,
/// Large binary files (.onnx, .bin, .safetensors, .gguf)
/// Returns: `LargeFile` (memory-mapped, always async)
LargeModel,
/// Small binary files (all other extensions)
/// Returns: `Data` (loaded into memory)
Data,
}
impl AssetKind {
/// Infer asset kind from file extension.
///
/// # Examples
///
/// ```
/// use waterui_assets::AssetKind;
///
/// assert_eq!(AssetKind::from_extension("png"), AssetKind::Image);
/// assert_eq!(AssetKind::from_extension("mp4"), AssetKind::Video);
/// assert_eq!(AssetKind::from_extension("onnx"), AssetKind::LargeModel);
/// assert_eq!(AssetKind::from_extension("json"), AssetKind::Data);
/// ```
#[must_use]
pub const fn from_extension(ext: &str) -> Self {
// Note: const fn cannot use match on &str directly in stable Rust,
// so we use a helper approach
match ext.as_bytes() {
// Font extensions
b"ttf" | b"otf" | b"ttc" | b"otc" | b"woff" | b"woff2" => Self::Font,
// Image extensions
b"png" | b"jpg" | b"jpeg" | b"gif" | b"webp" | b"avif" | b"bmp" | b"ico" | b"svg" => {
Self::Image
}
// Video extensions
b"mp4" | b"mov" | b"webm" | b"avi" | b"mkv" => Self::Video,
// Audio extensions
b"mp3" | b"wav" | b"ogg" | b"flac" | b"aac" | b"m4a" => Self::Audio,
// Large model extensions (ML models, large binaries)
b"onnx" | b"safetensors" | b"gguf" | b"ggml" | b"pt" | b"pth" | b"bin" => {
Self::LargeModel
}
// Everything else is Data
_ => Self::Data,
}
}
/// Check if this asset kind requires async loading.
///
/// - `LargeModel` always requires async (mmap setup)
/// - `Data` requires async only for remote URLs
/// - Media types (Image, Video, Audio) are always sync (URL-based)
#[must_use]
pub const fn requires_async_local(&self) -> bool {
matches!(self, Self::LargeModel)
}
/// Check if this asset kind supports embed mode.
///
/// Only `Data` supports `embed = true` in the `asset!` macro.
#[must_use]
pub const fn supports_embed(&self) -> bool {
matches!(self, Self::Data)
}
/// Returns true when this asset kind represents a font.
#[must_use]
pub const fn is_font(&self) -> bool {
matches!(self, Self::Font)
}
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_image_extensions() {
assert_eq!(AssetKind::from_extension("png"), AssetKind::Image);
assert_eq!(AssetKind::from_extension("jpg"), AssetKind::Image);
assert_eq!(AssetKind::from_extension("jpeg"), AssetKind::Image);
assert_eq!(AssetKind::from_extension("gif"), AssetKind::Image);
assert_eq!(AssetKind::from_extension("webp"), AssetKind::Image);
assert_eq!(AssetKind::from_extension("svg"), AssetKind::Image);
}
#[test]
fn test_video_extensions() {
assert_eq!(AssetKind::from_extension("mp4"), AssetKind::Video);
assert_eq!(AssetKind::from_extension("mov"), AssetKind::Video);
assert_eq!(AssetKind::from_extension("webm"), AssetKind::Video);
}
#[test]
fn test_audio_extensions() {
assert_eq!(AssetKind::from_extension("mp3"), AssetKind::Audio);
assert_eq!(AssetKind::from_extension("wav"), AssetKind::Audio);
assert_eq!(AssetKind::from_extension("ogg"), AssetKind::Audio);
}
#[test]
fn test_font_extensions() {
assert_eq!(AssetKind::from_extension("ttf"), AssetKind::Font);
assert_eq!(AssetKind::from_extension("otf"), AssetKind::Font);
assert_eq!(AssetKind::from_extension("woff2"), AssetKind::Font);
}
#[test]
fn test_large_model_extensions() {
assert_eq!(AssetKind::from_extension("onnx"), AssetKind::LargeModel);
assert_eq!(
AssetKind::from_extension("safetensors"),
AssetKind::LargeModel
);
assert_eq!(AssetKind::from_extension("gguf"), AssetKind::LargeModel);
assert_eq!(AssetKind::from_extension("bin"), AssetKind::LargeModel);
}
#[test]
fn test_data_extensions() {
assert_eq!(AssetKind::from_extension("json"), AssetKind::Data);
assert_eq!(AssetKind::from_extension("toml"), AssetKind::Data);
assert_eq!(AssetKind::from_extension("wgsl"), AssetKind::Data);
assert_eq!(AssetKind::from_extension("txt"), AssetKind::Data);
}
}