manganis_core/
asset.rs

1use crate::AssetOptions;
2use const_serialize::{ConstStr, SerializeConst};
3use std::path::PathBuf;
4
5/// An asset that should be copied by the bundler with some options. This type will be
6/// serialized into the binary and added to the link section [`LinkSection::CURRENT`](crate::linker::LinkSection::CURRENT).
7/// CLIs that support manganis, should pull out the assets from the link section, optimize,
8/// and write them to the filesystem at [`BundledAsset::bundled_path`] for the application
9/// to use.
10#[derive(
11    Debug,
12    PartialEq,
13    PartialOrd,
14    Clone,
15    Copy,
16    Hash,
17    SerializeConst,
18    serde::Serialize,
19    serde::Deserialize,
20)]
21pub struct BundledAsset {
22    /// The absolute path of the asset
23    absolute_source_path: ConstStr,
24    /// The bundled path of the asset
25    bundled_path: ConstStr,
26    /// The options for the asset
27    options: AssetOptions,
28}
29
30impl BundledAsset {
31    #[doc(hidden)]
32    /// This should only be called from the macro
33    /// Create a new asset
34    pub const fn new(
35        absolute_source_path: &'static str,
36        bundled_path: &'static str,
37        options: AssetOptions,
38    ) -> Self {
39        Self {
40            absolute_source_path: ConstStr::new(absolute_source_path),
41            bundled_path: ConstStr::new(bundled_path),
42            options,
43        }
44    }
45
46    #[doc(hidden)]
47    /// This should only be called from the macro
48    /// Create a new asset but with a relative path
49    ///
50    /// This method is deprecated and will be removed in a future release.
51    #[deprecated(
52        note = "Relative asset!() paths are not supported. Use a path like `/assets/myfile.png` instead of `./assets/myfile.png`"
53    )]
54    pub const fn new_relative(
55        absolute_source_path: &'static str,
56        bundled_path: &'static str,
57        options: AssetOptions,
58    ) -> Self {
59        Self::new(absolute_source_path, bundled_path, options)
60    }
61
62    #[doc(hidden)]
63    /// This should only be called from the macro
64    /// Create a new asset from const paths
65    pub const fn new_from_const(
66        absolute_source_path: ConstStr,
67        bundled_path: ConstStr,
68        options: AssetOptions,
69    ) -> Self {
70        Self {
71            absolute_source_path,
72            bundled_path,
73            options,
74        }
75    }
76
77    /// Get the bundled name of the asset. This identifier cannot be used to read the asset directly
78    pub fn bundled_path(&self) -> &str {
79        self.bundled_path.as_str()
80    }
81
82    /// Get the absolute path of the asset source. This path will not be available when the asset is bundled
83    pub fn absolute_source_path(&self) -> &str {
84        self.absolute_source_path.as_str()
85    }
86    /// Get the options for the asset
87    pub const fn options(&self) -> &AssetOptions {
88        &self.options
89    }
90}
91
92/// A bundled asset with some options. The asset can be used in rsx! to reference the asset.
93/// It should not be read directly with [`std::fs::read`] because the path needs to be resolved
94/// relative to the bundle
95///
96/// ```rust
97/// # use manganis::{asset, Asset};
98/// # use dioxus::prelude::*;
99/// const ASSET: Asset = asset!("/assets/image.png");
100/// rsx! {
101///     img { src: ASSET }
102/// };
103/// ```
104#[derive(Debug, PartialEq, Clone, Copy)]
105pub struct Asset {
106    /// The bundled asset
107    bundled: BundledAsset,
108    /// The link section for the asset
109    keep_link_section: fn() -> u8,
110}
111
112impl Asset {
113    #[doc(hidden)]
114    /// This should only be called from the macro
115    /// Create a new asset from the bundled form of the asset and the link section
116    pub const fn new(bundled: BundledAsset, keep_link_section: fn() -> u8) -> Self {
117        Self {
118            bundled,
119            keep_link_section,
120        }
121    }
122
123    /// Get the bundled asset
124    pub const fn bundled(&self) -> &BundledAsset {
125        &self.bundled
126    }
127
128    /// Return a canonicalized path to the asset
129    ///
130    /// Attempts to resolve it against an `assets` folder in the current directory.
131    /// If that doesn't exist, it will resolve against the cargo manifest dir
132    pub fn resolve(&self) -> PathBuf {
133        // Force a volatile read of the asset link section to ensure the symbol makes it into the binary
134        (self.keep_link_section)();
135
136        #[cfg(feature = "dioxus")]
137        // If the asset is relative, we resolve the asset at the current directory
138        if !dioxus_core_types::is_bundled_app() {
139            return PathBuf::from(self.bundled.absolute_source_path.as_str());
140        }
141
142        #[cfg(feature = "dioxus")]
143        let bundle_root = {
144            let base_path = dioxus_cli_config::base_path();
145            let base_path = base_path
146                .as_deref()
147                .map(|base_path| {
148                    let trimmed = base_path.trim_matches('/');
149                    format!("/{trimmed}")
150                })
151                .unwrap_or_default();
152            PathBuf::from(format!("{base_path}/assets/"))
153        };
154        #[cfg(not(feature = "dioxus"))]
155        let bundle_root = PathBuf::from("/assets/");
156
157        // Otherwise presumably we're bundled and we can use the bundled path
158        bundle_root.join(PathBuf::from(
159            self.bundled.bundled_path.as_str().trim_start_matches('/'),
160        ))
161    }
162}
163
164impl From<Asset> for String {
165    fn from(value: Asset) -> Self {
166        value.to_string()
167    }
168}
169impl From<Asset> for Option<String> {
170    fn from(value: Asset) -> Self {
171        Some(value.to_string())
172    }
173}
174
175impl std::fmt::Display for Asset {
176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177        write!(f, "{}", self.resolve().display())
178    }
179}
180
181#[cfg(feature = "dioxus")]
182impl dioxus_core_types::DioxusFormattable for Asset {
183    fn format(&self) -> std::borrow::Cow<'static, str> {
184        std::borrow::Cow::Owned(self.to_string())
185    }
186}