1use crate::AssetOptions;
2use const_serialize::{ConstStr, SerializeConst, deserialize_const};
3use std::{fmt::Debug, hash::Hash, path::PathBuf};
4
5#[derive(Debug, Eq, Clone, Copy, SerializeConst, serde::Serialize, serde::Deserialize)]
11pub struct BundledAsset {
12 absolute_source_path: ConstStr,
14
15 bundled_path: ConstStr,
17
18 options: AssetOptions,
20}
21
22impl PartialEq for BundledAsset {
23 fn eq(&self, other: &Self) -> bool {
24 self.absolute_source_path == other.absolute_source_path
25 && self.bundled_path == other.bundled_path
26 && self.options == other.options
27 }
28}
29
30impl PartialOrd for BundledAsset {
31 fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
32 match self
33 .absolute_source_path
34 .partial_cmp(&other.absolute_source_path)
35 {
36 Some(core::cmp::Ordering::Equal) => {}
37 ord => return ord,
38 }
39 match self.bundled_path.partial_cmp(&other.bundled_path) {
40 Some(core::cmp::Ordering::Equal) => {}
41 ord => return ord,
42 }
43 self.options.partial_cmp(&other.options)
44 }
45}
46
47impl Hash for BundledAsset {
48 fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
49 self.absolute_source_path.hash(state);
50 self.bundled_path.hash(state);
51 self.options.hash(state);
52 }
53}
54
55impl BundledAsset {
56 pub const PLACEHOLDER_HASH: &str = "This should be replaced by dx as part of the build process. If you see this error, make sure you are using a matching version of dx and dioxus and you are not stripping symbols from your binary.";
57
58 #[doc(hidden)]
59 pub const fn new(
62 absolute_source_path: &str,
63 bundled_path: &str,
64 options: AssetOptions,
65 ) -> Self {
66 Self {
67 absolute_source_path: ConstStr::new(absolute_source_path),
68 bundled_path: ConstStr::new(bundled_path),
69 options,
70 }
71 }
72
73 pub fn bundled_path(&self) -> &str {
75 self.bundled_path.as_str()
76 }
77
78 pub fn absolute_source_path(&self) -> &str {
80 self.absolute_source_path.as_str()
81 }
82
83 pub const fn options(&self) -> &AssetOptions {
85 &self.options
86 }
87}
88
89#[allow(unpredictable_function_pointer_comparisons)]
102#[derive(PartialEq, Clone, Copy)]
103pub struct Asset {
104 bundled: fn() -> &'static [u8],
113}
114
115impl Debug for Asset {
116 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
117 self.resolve().fmt(f)
118 }
119}
120
121unsafe impl Send for Asset {}
122unsafe impl Sync for Asset {}
123
124impl Asset {
125 #[doc(hidden)]
126 pub const fn new(bundled: extern "Rust" fn() -> &'static [u8]) -> Self {
129 Self { bundled }
130 }
131
132 pub fn bundled(&self) -> BundledAsset {
134 let bundled = (self.bundled)();
137 let ptr = bundled as *const [u8] as *const u8;
138 let len = bundled.len();
139 if ptr.is_null() {
140 panic!(
141 "Tried to use an asset that was not bundled. Make sure you are compiling dx as the linker"
142 );
143 }
144 let mut bytes = Vec::with_capacity(len);
145 for byte in 0..len {
146 let byte = unsafe { std::ptr::read_volatile(ptr.add(byte)) };
149 bytes.push(byte);
150 }
151
152 deserialize_const!(BundledAsset, bytes.as_slice()).expect("Failed to deserialize asset. Make sure you built with the matching version of the Dioxus CLI").1
153 }
154
155 pub fn resolve(&self) -> PathBuf {
160 #[cfg(feature = "dioxus")]
161 if !dioxus_core_types::is_bundled_app() {
163 return PathBuf::from(self.bundled().absolute_source_path.as_str());
164 }
165
166 #[cfg(feature = "dioxus")]
167 let bundle_root = {
168 let base_path = dioxus_cli_config::base_path();
169 let base_path = base_path
170 .as_deref()
171 .map(|base_path| {
172 let trimmed = base_path.trim_matches('/');
173 format!("/{trimmed}")
174 })
175 .unwrap_or_default();
176 PathBuf::from(format!("{base_path}/assets/"))
177 };
178 #[cfg(not(feature = "dioxus"))]
179 let bundle_root = PathBuf::from("/assets/");
180
181 bundle_root.join(PathBuf::from(
183 self.bundled().bundled_path.as_str().trim_start_matches('/'),
184 ))
185 }
186}
187
188impl From<Asset> for String {
189 fn from(value: Asset) -> Self {
190 value.to_string()
191 }
192}
193impl From<Asset> for Option<String> {
194 fn from(value: Asset) -> Self {
195 Some(value.to_string())
196 }
197}
198
199impl std::fmt::Display for Asset {
200 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
201 write!(f, "{}", self.resolve().display())
202 }
203}
204
205#[cfg(feature = "dioxus")]
206impl dioxus_core_types::DioxusFormattable for Asset {
207 fn format(&self) -> std::borrow::Cow<'static, str> {
208 std::borrow::Cow::Owned(self.to_string())
209 }
210}