Bundle
BundleAssetProtocol
allows to create easy custom bytes decoders.
use keket::{
database::{path::AssetPath, AssetDatabase},
fetch::file::FileAssetFetch,
protocol::{bundle::BundleAssetProtocol, bytes::BytesAssetProtocol, text::TextAssetProtocol},
};
use serde::Deserialize;
use serde_json::Value;
use std::{error::Error, fs::Metadata, path::PathBuf};
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct Person {
age: u8,
home: PersonHome,
friends: Vec<String>,
}
#[derive(Debug, Deserialize)]
#[allow(dead_code)]
struct PersonHome {
country: String,
address: String,
}
fn main() -> Result<(), Box<dyn Error>> {
let mut database = AssetDatabase::default()
// Asset protocols tell how to deserialize bytes into assets.
.with_protocol(TextAssetProtocol)
.with_protocol(BytesAssetProtocol)
// Bundle protocol allows for easly making a protocol that takes
// bytes and returns bundle with optional dependencies.
.with_protocol(BundleAssetProtocol::new("json", |bytes: Vec<u8>| {
let asset = serde_json::from_slice::<Value>(&bytes)?;
Ok((asset,).into())
}))
.with_protocol(BundleAssetProtocol::new("person", |bytes: Vec<u8>| {
let asset = serde_json::from_slice::<Person>(&bytes)?;
Ok((asset,).into())
}))
// Asset fetch tells how to get bytes from specific source.
.with_fetch(FileAssetFetch::default().with_root("resources"));
// Ensure method either gives existing asset handle or creates new
// and loads asset if not existing yet in storage.
let lorem = database.ensure("text://lorem.txt")?;
// Accessing component(s) of asset entry.
// Assets can store multiple data associated to them, consider them meta data.
println!("Lorem Ipsum: {}", lorem.access::<&String>(&database));
let json = database.ensure("json://person.json")?;
println!("JSON: {:#}", json.access::<&Value>(&database));
let person = database.ensure("person://person.json")?;
println!("Person: {:#?}", person.access::<&Person>(&database));
let trash = database.ensure("bytes://trash.bin")?;
println!("Bytes: {:?}", trash.access::<&Vec<u8>>(&database));
// We can query storage for asset components to process assets, just like with ECS.
for (asset_path, file_path, metadata) in database
.storage
.query::<true, (&AssetPath, &PathBuf, &Metadata)>()
{
println!(
"Asset: `{}` at location: {:?} has metadata: {:#?}",
asset_path, file_path, metadata
);
}
Ok(())
}
We can use closures or any type that implements BundleWithDependenciesProcessor
trait, which turns input bytes into BundleWithDependencies
container with output
components bundle and optional list of dependencies to schedule after decoding.
use keket::{
database::{path::AssetPathStatic, AssetDatabase},
fetch::file::FileAssetFetch,
protocol::bundle::{
BundleAssetProtocol, BundleWithDependencies, BundleWithDependenciesProcessor,
},
};
use serde::Deserialize;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut database = AssetDatabase::default()
// Register custom asset processor.
.with_protocol(BundleAssetProtocol::new("custom", CustomAssetProcessor))
.with_fetch(FileAssetFetch::default().with_root("resources"));
// Ensure custom asset existence.
let handle = database.ensure("custom://part1.json")?;
// Make database process loaded dependencies.
while database.is_busy() {
database.maintain()?;
}
let contents = handle.access::<&CustomAsset>(&database).contents(&database);
println!("Custom chain contents: {:?}", contents);
Ok(())
}
// Custom asset type.
#[derive(Debug, Default, Deserialize)]
struct CustomAsset {
// Main content.
content: String,
// Optional sibling asset (content continuation).
#[serde(default)]
next: Option<AssetPathStatic>,
}
impl CustomAsset {
fn contents(&self, database: &AssetDatabase) -> String {
// Read this and it's siblings content to output.
let mut result = String::new();
let mut current = Some(self);
while let Some(asset) = current {
result.push_str(asset.content.as_str());
current = current
.as_ref()
.and_then(|asset| asset.next.as_ref())
.and_then(|path| path.find(database))
.and_then(|handle| handle.access_checked::<&Self>(database));
if current.is_some() {
result.push(' ');
}
}
result
}
}
struct CustomAssetProcessor;
impl BundleWithDependenciesProcessor for CustomAssetProcessor {
// Bundle of asset components this asset processor produces from processed asset.
type Bundle = (CustomAsset,);
fn process_bytes(
&mut self,
bytes: Vec<u8>,
) -> Result<BundleWithDependencies<Self::Bundle>, Box<dyn Error>> {
let asset = serde_json::from_slice::<CustomAsset>(&bytes)?;
let dependency = asset.next.clone();
// Return bundled components and optional dependency which will be additionally loaded.
Ok(BundleWithDependencies::new((asset,)).maybe_dependency(dependency))
}
}
use keket::{
database::{path::AssetPathStatic, AssetDatabase},
fetch::file::FileAssetFetch,
protocol::bundle::{
BundleAssetProtocol, BundleWithDependencies, BundleWithDependenciesProcessor,
},
};
use serde::Deserialize;
use std::error::Error;
fn main() -> Result<(), Box<dyn Error>> {
let mut database = AssetDatabase::default()
// Register custom asset processor.
.with_protocol(BundleAssetProtocol::new("custom", CustomAssetProcessor))
.with_fetch(FileAssetFetch::default().with_root("resources"));
// Ensure custom asset existence.
let handle = database.ensure("custom://part1.json")?;
// Make database process loaded dependencies.
while database.is_busy() {
database.maintain()?;
}
let contents = handle.access::<&CustomAsset>(&database).contents(&database);
println!("Custom chain contents: {:?}", contents);
Ok(())
}
// Custom asset type.
#[derive(Debug, Default, Deserialize)]
struct CustomAsset {
// Main content.
content: String,
// Optional sibling asset (content continuation).
#[serde(default)]
next: Option<AssetPathStatic>,
}
impl CustomAsset {
fn contents(&self, database: &AssetDatabase) -> String {
// Read this and it's siblings content to output.
let mut result = String::new();
let mut current = Some(self);
while let Some(asset) = current {
result.push_str(asset.content.as_str());
current = current
.as_ref()
.and_then(|asset| asset.next.as_ref())
.and_then(|path| path.find(database))
.and_then(|handle| handle.access_checked::<&Self>(database));
if current.is_some() {
result.push(' ');
}
}
result
}
}
struct CustomAssetProcessor;
impl BundleWithDependenciesProcessor for CustomAssetProcessor {
// Bundle of asset components this asset processor produces from processed asset.
type Bundle = (CustomAsset,);
fn process_bytes(
&mut self,
bytes: Vec<u8>,
) -> Result<BundleWithDependencies<Self::Bundle>, Box<dyn Error>> {
let asset = serde_json::from_slice::<CustomAsset>(&bytes)?;
let dependency = asset.next.clone();
// Return bundled components and optional dependency which will be additionally loaded.
Ok(BundleWithDependencies::new((asset,)).maybe_dependency(dependency))
}
}