Files
dioxus/packages/manganis/manganis-core/src/asset.rs
T
Evan Almloff 3d62fbf021 Fix base path on web and fullstack (#3247)
* Create a crate for constant serialization of config structs for manganis

* use SerializeConst for the image asset builder

* switch to a declarative macro for assets

* clean up asset macro a bit

* add serializable options for each asset type

* serialize const vec

* Add unique path formatting

* implement the new manganis macro

* optimize assets in the CLI

* Fix clippy

* Fix assets with dioxus formattable

* reduce fuzzing test limits

* use the new syntax in examples

* fix formatting

* Final doc and test pass on const-serialize

* fix avif support

* Fix manganis doc tests

* cache asset optimization

* Split out asset and bundled asset

* make hash pointer width independent

* remove const serialize cargo lock

* Fix manganis macro docs

* all tests passing

* add constvec::is_empty method to fix clippy lint

* remove nasm feature

* simplify test_rsplit_once test so typos passes

* fix range syntax for stable

* revert example change from clippy fix

* remove dioxus-static-site-generation workspace dependency

* always accept unix paths

* fix windows path seperator

* fix folder asset hash

* Optimize assets in a blocking task

* Fix asset options docs

* Document Asset and BundledAsset

* move manganis back into it's own folder

* simplify the linker macro a bit

* add more docs to AssetParser expansion

* fix manganis core doc test

* add image format helpers

* Fill in new cargo.tomls

* fix folders with explicit options

* Split by  both unix and windows path separators and take the smallest one

* fix string length

* Fix base path on web and fullstack

* Fix assets with base path

* fix fullstack base route with trailing slash

* fix clippy
2024-11-27 10:40:55 +03:00

172 lines
5.3 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)
}
/// 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())
}
}