Skip to main content

28_protocol_extract/
28_protocol_extract.rs

1use keket::{
2    database::{
3        AssetDatabase,
4        handle::AssetHandle,
5        path::{AssetPath, AssetPathStatic},
6    },
7    fetch::file::FileAssetFetch,
8    protocol::AssetProtocol,
9    third_party::anput::{bundle::DynamicBundle, world::World},
10};
11use std::error::Error;
12
13fn main() -> Result<(), Box<dyn Error>> {
14    let mut database = AssetDatabase::default()
15        // Register custom asset protocol.
16        .with_protocol(CustomAssetProtocol)
17        .with_fetch(FileAssetFetch::default().with_root("resources"))
18        .with_event(|event| {
19            println!("Asset closure event: {event:#?}");
20            Ok(())
21        });
22
23    // We spawn an asset with configuration meta to be extracted into asset
24    // components, as well as path being rewritten to not contain meta values.
25    database.ensure("custom://lorem.txt?uppercase")?;
26
27    while database.is_busy() {
28        database.maintain()?;
29    }
30
31    // Accessing asset and its extracted meta data via shortened path.
32    let handle = database.find("custom://lorem.txt").unwrap();
33    let (contents, meta) = handle.access::<(&String, &Meta)>(&database);
34    println!("Custom asset meta: {meta:?}");
35    println!("Custom asset contents: {contents:?}");
36
37    Ok(())
38}
39
40#[derive(Debug, Default)]
41struct Meta {
42    uppercase: bool,
43}
44
45struct CustomAssetProtocol;
46
47impl AssetProtocol for CustomAssetProtocol {
48    fn name(&self) -> &str {
49        "custom"
50    }
51
52    // Allows to extract initial configuration from asset path meta data.
53    fn extract_bundle_from_path(&self, path: &AssetPath) -> Result<DynamicBundle, Box<dyn Error>> {
54        let mut meta = Meta::default();
55        if path.has_meta_key("uppercase") {
56            meta.uppercase = true;
57        }
58        Ok(DynamicBundle::new(meta).unwrap())
59    }
60
61    // Allows to remove meta values from spawned asset path.
62    fn rewrite_path(&self, path: AssetPathStatic) -> Result<AssetPathStatic, Box<dyn Error>> {
63        Ok(AssetPathStatic::from_parts(
64            path.protocol(),
65            path.path(),
66            "",
67        ))
68    }
69
70    fn process_bytes(
71        &mut self,
72        handle: AssetHandle,
73        storage: &mut World,
74        bytes: Vec<u8>,
75    ) -> Result<(), Box<dyn Error>> {
76        let uppercase = storage.component::<true, Meta>(handle.entity())?.uppercase;
77        let mut contents = String::from_utf8(bytes)?;
78        if uppercase {
79            contents = contents.to_uppercase();
80        }
81        storage.insert(handle.entity(), (contents,))?;
82        Ok(())
83    }
84}