Files
dioxus/packages/manganis/manganis-core/src/asset.rs
T

187 lines
5.7 KiB
Rust

use crate::AssetOptions;
use const_serialize::{ConstStr, SerializeConst};
use std::path::PathBuf;
/// An asset that should be copied by the bundler with some options. This type will be
/// serialized into the binary and added to the link section [`LinkSection::CURRENT`](crate::linker::LinkSection::CURRENT).
/// CLIs that support manganis, should pull out the assets from the link section, optimize,
/// and write them to the filesystem at [`BundledAsset::bundled_path`] for the application
/// to use.
#[derive(
Debug,
PartialEq,
PartialOrd,
Clone,
Copy,
Hash,
SerializeConst,
serde::Serialize,
serde::Deserialize,
)]
pub struct BundledAsset {
/// The absolute path of the asset
absolute_source_path: ConstStr,
/// The bundled path of the asset
bundled_path: ConstStr,
/// The options for the asset
options: AssetOptions,
}
impl BundledAsset {
#[doc(hidden)]
/// This should only be called from the macro
/// Create a new asset
pub const fn new(
absolute_source_path: &'static str,
bundled_path: &'static str,
options: AssetOptions,
) -> Self {
Self {
absolute_source_path: ConstStr::new(absolute_source_path),
bundled_path: ConstStr::new(bundled_path),
options,
}
}
#[doc(hidden)]
/// This should only be called from the macro
/// Create a new asset but with a relative path
///
/// This method is deprecated and will be removed in a future release.
#[deprecated(
note = "Relative asset!() paths are not supported. Use a path like `/assets/myfile.png` instead of `./assets/myfile.png`"
)]
pub const fn new_relative(
absolute_source_path: &'static str,
bundled_path: &'static str,
options: AssetOptions,
) -> Self {
Self::new(absolute_source_path, bundled_path, options)
}
#[doc(hidden)]
/// This should only be called from the macro
/// Create a new asset from const paths
pub const fn new_from_const(
absolute_source_path: ConstStr,
bundled_path: ConstStr,
options: AssetOptions,
) -> Self {
Self {
absolute_source_path,
bundled_path,
options,
}
}
/// Get the bundled name of the asset. This identifier cannot be used to read the asset directly
pub fn bundled_path(&self) -> &str {
self.bundled_path.as_str()
}
/// Get the absolute path of the asset source. This path will not be available when the asset is bundled
pub fn absolute_source_path(&self) -> &str {
self.absolute_source_path.as_str()
}
/// Get the options for the asset
pub const fn options(&self) -> &AssetOptions {
&self.options
}
}
/// A bundled asset with some options. The asset can be used in rsx! to reference the asset.
/// It should not be read directly with [`std::fs::read`] because the path needs to be resolved
/// relative to the bundle
///
/// ```rust
/// # use manganis::{asset, Asset};
/// # use dioxus::prelude::*;
/// const ASSET: Asset = asset!("/assets/image.png");
/// rsx! {
/// img { src: ASSET }
/// };
/// ```
#[derive(Debug, PartialEq, Clone, Copy)]
pub struct Asset {
/// The bundled asset
bundled: BundledAsset,
/// The link section for the asset
keep_link_section: fn() -> u8,
}
impl Asset {
#[doc(hidden)]
/// This should only be called from the macro
/// Create a new asset from the bundled form of the asset and the link section
pub const fn new(bundled: BundledAsset, keep_link_section: fn() -> u8) -> Self {
Self {
bundled,
keep_link_section,
}
}
/// Get the bundled asset
pub const fn bundled(&self) -> &BundledAsset {
&self.bundled
}
/// Return a canonicalized path to the asset
///
/// Attempts to resolve it against an `assets` folder in the current directory.
/// If that doesn't exist, it will resolve against the cargo manifest dir
pub fn resolve(&self) -> PathBuf {
// Force a volatile read of the asset link section to ensure the symbol makes it into the binary
(self.keep_link_section)();
#[cfg(feature = "dioxus")]
// If the asset is relative, we resolve the asset at the current directory
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/");
// Otherwise presumably we're bundled and we can use the bundled path
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())
}
}