use crate::AssetOptions;
use const_serialize_07 as const_serialize;
use const_serialize_08::{deserialize_const, ConstStr, SerializeConst};
use std::{fmt::Debug, hash::Hash, path::PathBuf};
#[derive(
Debug,
Eq,
Clone,
Copy,
SerializeConst,
const_serialize::SerializeConst,
serde::Serialize,
serde::Deserialize,
)]
#[const_serialize(crate = const_serialize_08)]
pub struct BundledAsset {
absolute_source_path: ConstStr,
bundled_path: ConstStr,
options: AssetOptions,
}
impl PartialEq for BundledAsset {
fn eq(&self, other: &Self) -> bool {
self.absolute_source_path == other.absolute_source_path
&& self.bundled_path == other.bundled_path
&& self.options == other.options
}
}
impl PartialOrd for BundledAsset {
fn partial_cmp(&self, other: &Self) -> Option<std::cmp::Ordering> {
match self
.absolute_source_path
.partial_cmp(&other.absolute_source_path)
{
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
match self.bundled_path.partial_cmp(&other.bundled_path) {
Some(core::cmp::Ordering::Equal) => {}
ord => return ord,
}
self.options.partial_cmp(&other.options)
}
}
impl Hash for BundledAsset {
fn hash<H: std::hash::Hasher>(&self, state: &mut H) {
self.absolute_source_path.hash(state);
self.bundled_path.hash(state);
self.options.hash(state);
}
}
impl BundledAsset {
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.";
#[doc(hidden)]
pub const fn new(
absolute_source_path: &str,
bundled_path: &str,
options: AssetOptions,
) -> Self {
Self {
absolute_source_path: ConstStr::new(absolute_source_path),
bundled_path: ConstStr::new(bundled_path),
options,
}
}
pub fn bundled_path(&self) -> &str {
self.bundled_path.as_str()
}
pub fn absolute_source_path(&self) -> &str {
self.absolute_source_path.as_str()
}
pub const fn options(&self) -> &AssetOptions {
&self.options
}
}
#[allow(unpredictable_function_pointer_comparisons)]
#[derive(PartialEq, Clone, Copy)]
pub struct Asset {
bundled: fn() -> &'static [u8],
legacy: fn() -> &'static [u8],
}
impl Debug for Asset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
self.resolve().fmt(f)
}
}
unsafe impl Send for Asset {}
unsafe impl Sync for Asset {}
impl Asset {
#[doc(hidden)]
pub const fn new(
bundled: extern "Rust" fn() -> &'static [u8],
legacy: extern "Rust" fn() -> &'static [u8],
) -> Self {
Self { bundled, legacy }
}
pub fn bundled(&self) -> BundledAsset {
fn read_slice_volatile(bundled: &'static [u8]) -> Vec<u8> {
let ptr = bundled as *const [u8] as *const u8;
let len = bundled.len();
if ptr.is_null() {
panic!("Tried to use an asset that was not bundled. Make sure you are compiling dx as the linker");
}
let mut bytes = Vec::with_capacity(len);
for byte in 0..len {
let byte = unsafe { std::ptr::read_volatile(ptr.add(byte)) };
bytes.push(byte);
}
bytes
}
let bundled = (self.bundled)();
let bytes = read_slice_volatile(bundled);
let read = bytes.as_slice();
let asset = deserialize_const!(BundledAsset, read).expect("Failed to deserialize asset. Make sure you built with the matching version of the Dioxus CLI").1;
if asset.bundled_path() == BundledAsset::PLACEHOLDER_HASH {
let bundled = (self.legacy)();
let bytes = read_slice_volatile(bundled);
let read = const_serialize_07::ConstReadBuffer::new(bytes.as_ref());
let asset = const_serialize_07::deserialize_const!(BundledAsset, read).expect("Failed to deserialize asset. Make sure you built with the matching version of the Dioxus CLI").1;
asset
} else {
asset
}
}
pub fn resolve(&self) -> PathBuf {
#[cfg(feature = "dioxus")]
if !dioxus_core_types::is_bundled_app() {
return PathBuf::from(self.bundled().absolute_source_path.as_str());
}
#[cfg(feature = "dioxus")]
let bundle_root = {
let base_path = dioxus_cli_config::base_path();
let base_path = base_path
.as_deref()
.map(|base_path| {
let trimmed = base_path.trim_matches('/');
format!("/{trimmed}")
})
.unwrap_or_default();
PathBuf::from(format!("{base_path}/assets/"))
};
#[cfg(not(feature = "dioxus"))]
let bundle_root = PathBuf::from("/assets/");
bundle_root.join(PathBuf::from(
self.bundled().bundled_path.as_str().trim_start_matches('/'),
))
}
}
impl From<Asset> for String {
fn from(value: Asset) -> Self {
value.to_string()
}
}
impl From<Asset> for Option<String> {
fn from(value: Asset) -> Self {
Some(value.to_string())
}
}
impl std::fmt::Display for Asset {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(f, "{}", self.resolve().display())
}
}
#[cfg(feature = "dioxus")]
impl dioxus_core_types::DioxusFormattable for Asset {
fn format(&self) -> std::borrow::Cow<'static, str> {
std::borrow::Cow::Owned(self.to_string())
}
}