notes/architecture/08-ASSETS.md
Manganis provides compile-time asset management through the asset!() macro, enabling type-safe asset references with automatic optimization and cache-busting.
asset!("/assets/image.png", AssetOptions::image())
↓
1. Path Resolution
- Resolves relative to CARGO_MANIFEST_DIR
- Validates path exists and stays within crate
2. File Hashing
- Creates DefaultHasher from path + options + span
- Produces 16-character hex hash
3. Generate Link Section
- Creates __ASSETS__{hash} symbol
- Creates __MANGANIS__{hash} for legacy CLI
- Both use CBOR serialization via const_serialize
4. Code Generation
- Emits BundledAsset const with PLACEHOLDER_HASH
- Creates Asset struct with function pointers
- Uses volatile reads to prevent optimization
Two static link sections for backward compatibility:
Legacy Format (__MANGANIS__{hash}):
New Format (__ASSETS__{hash}):
Both start with PLACEHOLDER_HASH sentinel, replaced by CLI during build.
AssetOptions
├── ImageAssetOptions
│ ├── format: ImageFormat (Png, Jpg, Webp, Avif)
│ ├── size: ImageSize (Manual | Automatic)
│ └── preload: bool
│
├── CssAssetOptions
│ ├── minify: bool (default: true)
│ ├── preload: bool
│ └── static_head: bool
│
├── JsAssetOptions
│ ├── minify: bool (default: true)
│ ├── preload: bool
│ └── static_head: bool
│
├── CssModuleAssetOptions
│ ├── minify: bool
│ └── preload: bool
│
├── FolderAssetOptions
│ └── (no custom options)
│
└── Unknown (generic binary)
All options serializable at const-time via SerializeConst derive.
css_module!(Styles = "/my.module.css", AssetOptions::css_module());
// Generates:
struct Styles {}
impl Styles {
pub const header: &str = "abc[hash]"; // Unique scoped class
pub const button: &str = "def[hash]";
}
CSS Identifier Collection:
.className and #idName patternsSupported Major Types:
Not Supported: Tagged values (6), floating point (7)
Layout-based binary copying at compile time:
unsafe trait SerializeConst: Sized {
const MEMORY_LAYOUT: Layout;
}
enum Layout {
Enum(EnumLayout), // repr(C, u8) required
Struct(StructLayout), // Field offsets
Array(ArrayLayout), // Fixed-size
Primitive(PrimitiveLayout),
List(ListLayout), // Variable length
}
Process:
pub struct ConstStr {
bytes: [MaybeUninit<u8>; 256], // Fixed 256-byte buffer
len: u32,
}
Used for asset paths and CSS identifiers. Serialized as List layout.
Phase 1: Binary Scanning
1. Read compiled binary
2. Find __ASSETS__ symbols via objfile parser
3. Platform-specific methods:
- Native: object crate symbol tables
- Windows PE: PDB file parsing
- WASM: walrus data section parsing
- Android: NDK handling
Phase 2: Asset Deserialization
For each __ASSETS__{hash}:
1. Read serialized bytes from section
2. Deserialize BundledAsset via const_serialize_08
3. Fall back to const_serialize_07 if fails
4. Extract: absolute_source_path, bundled_path, options
Phase 3: Unique Asset Collection
Phase 4: Asset Optimization
| Type | Processing |
|---|---|
| Image | Resize, convert format, optimize |
| CSS | Minify if enabled |
| JS | Minify if enabled |
| Folder | Recursive copy |
| Unknown | Direct copy with optional hash |
Phase 5: Binary Patching
For each processed asset:
1. Compute final hash (content + options + version)
2. Create new BundledAsset with:
- bundled_path: "/assets/{output-filename}"
3. Serialize to CBOR
4. Locate __ASSETS__{hash} in binary
5. Overwrite bytes at symbol offset
Development Mode (!is_bundled_app()):
Asset::resolve()
→ Returns bundled().absolute_source_path
→ Browser/native accesses original files
Production Mode (is_bundled_app()):
Asset::resolve()
→ Constructs path from:
* base_path() (e.g., "/app")
* bundled_path from BundledAsset
→ Returns "/app/assets/{output-filename}"
Web (WASM):
resolve_web_asset(): Fetches via HTTP fetch() APIVec<u8>Desktop:
resolve_asset_path_from_filesystem()../Resources/assets/../lib/{product}/assets/assets/ (same directory as exe)Android:
to_java_load_asset(): Uses NDK AssetManager/data/local/tmp/dx/INPUT_HASH (macro generation):
= DefaultHasher(span.debug_string + options.string + asset_path)
Used for: __ASSETS__{INPUT_HASH} symbol
CONTENT_HASH (build time):
= Hash(source_contents + applied_options + manganis_version)
Used for: Final output filename
Example: image.png → image-a1b2c3d4e5f6g7h8.webp
CSS_MODULE_HASH:
= Hash(css_module_options + content_hash)
Used for: Scoped CSS identifiers
IMAGE: /assets/photo.png → /assets/photo-{hash}.webp
CSS/JS: /assets/style.css → /assets/style-{hash}.css
FOLDER: /assets (folder) → /assets/ (unchanged)
std::ptr::read_volatile() prevents optimizing away link section readsrepr(C, u8) for enums#[derive(SerializeConst, ...)]
pub struct VideoAssetOptions {
format: VideoFormat,
preload: bool,
}
pub enum AssetVariant {
Video(VideoAssetOptions),
}
pub const fn video() -> AssetOptionsBuilder<VideoAssetOptions> {
AssetOptionsBuilder::variant(VideoAssetOptions::default())
}
assets.rs):AssetVariant::Video(opts) => {
// Video-specific processing
}
IAAC (Infrastructure as Code):
const DATABASE_CONFIG: Asset = asset!("/config/db.yaml");
// CLI extracts, validates, deploys
secret!() Macro:
const API_KEY: Secret = secret!("DIOXUS_API_KEY");
// Compile-time validation
// Runtime injection from secure store
Legacy (0.7.0-0.7.1):
__MANGANIS__{hash}Current (0.7.2+):
__ASSETS__{hash}Migration: CLI tries new format first, falls back to legacy if deserialization fails or PLACEHOLDER_HASH still present.