1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133
//! # 🧰 Resource API
//!
//! This API is useful for embedding and accessing static resources in a module.
//!
//! ## Example usage
//!
//! In `Cargo.toml` for the module, define which static file resource the module wants to be able to access.
//!
//! ```toml
//! [package.metadata.ark]
//! include-files = [
//! "assets/coin.wav",
//! ]
//! ```
//!
//! This will associate the file "assets/coin.wav" as a dependent static resource of the Ark module and
//! Ark will load it and make it available for the module before it is loaded.
//!
//! Inside the module one can then use `ark::resource::get("assets/coin.wav")` to get a slice to the
//! contents of the resource. This data is always loaded in memory and as such always available for the module.
//!
//! ## File roots
//!
//! If one has a shared workspace with multiple modules and want to share resources between them, or have a more
//! complex path for them, one can set one or multiple file roots for the included files in `Cargo.toml`.
//!
//! In the below example we have an `assets` folder that is located in a shared workspace directory relative to the
//! `Cargo.toml` file and where we want to include files from. Note that in this example the resource would be referenced
//! in the module with the `ark::resource::get("assets/coin.wav")`
//!
//! ```toml
//! [package.metadata.ark]
//! include-file-roots = [
//! "../../assets",
//! include-files = [
//! "coin.wav",
//! ]
//! ```
//!
//! It is also possible to have multiple roots specified. The file will then be loaded from the first root it is found in
//!
//! ## Alternatives
//!
//! This mechanism is meant for resources that are not trivially small (say large than 1 KB) and enables Ark to compress, de-duplicate and
//! cache the resources efficiently for distribution, storage, and loading.
//!
//! But if one has very small file resources, where compression and caching is not as beneficial, then it can be more optimal and easier
//! to embed the file directly into the module binary instead with the Rust standard [`include_bytes!`] macro
use crate::ffi::ErrorCode;
#[doc(hidden)]
pub use crate::ffi::resource_v1::API as FFI_API;
pub use crate::ffi::resource_v1::ResourceHandleRepr;
/// Retrieves named static resource
pub fn get(name: &'static str) -> Option<&'static [u8]> {
get_dynamic(name)
}
/// Retrieves named static resource in a unsafe manner
///
/// Takes a non-'static str as parameter.
/// [`get`] should be preferred over this.
pub fn get_dynamic(name: &str) -> Option<&'static [u8]> {
match crate::ffi::resource_v0::get_static(name) {
Ok(r) => Some(unsafe { std::slice::from_raw_parts(r.ptr as *const u8, r.size as usize) }),
Err(ErrorCode::NotFound) => None,
Err(error_code) => panic!(
"Resource get '{}' failed with code {}",
name, error_code as u32
),
}
}
/// Represents a handle to unique resource
///
/// In-process serialization is supported through `speedy`. However, this
/// handle should not be persisted across module sessions.
#[derive(Clone, Debug, PartialEq, Eq, Hash)]
#[cfg_attr(feature = "with_speedy", derive(speedy::Writable, speedy::Readable))]
pub struct ResourceHandle {
inner: ResourceHandleRepr,
}
impl ResourceHandle {
/// Request the latest version of a resource given the provided name
///
/// This will start downloading the resource if it isn't present in Ark
#[inline]
pub fn request(name: &str) -> Self {
let inner = crate::ffi::resource_v1::request_resource_by_name(name);
Self { inner }
}
/// Retrieve the resource's data by copying it into Wasm memory
///
/// Returns [`None`] if the resource hasn't finished downloading
///
/// If you only want to check if a resource is ready, use [`Self::is_ready`] instead
#[inline]
pub fn try_retrieve(&self) -> Option<Vec<u8>> {
match crate::ffi::resource_v1::try_retrieve(self.inner) {
Ok(bytes) => Some(bytes),
Err(ErrorCode::Unavailable) => None,
Err(error_code) => panic!(
"Failed retrieving resource bytes with code: {}",
error_code as u32
), // TODO: display name?
}
}
/// Check if the resource is available for use
#[inline]
pub fn is_ready(&self) -> bool {
match crate::ffi::resource_v1::is_ready(self.inner) {
Ok(()) => true,
Err(ErrorCode::Unavailable) => false,
Err(error_code) => panic!("Failed `ResourceHandle::is_ready`: {}", error_code as u32),
}
}
/// Retrieve the raw inner [`ResourceHandleRepr`]
///
/// Useful for passing to other Ark APIs that can operate on resources
///
/// TODO: return a concrete type that can be shared between Ark APIs
#[inline]
pub fn raw(&self) -> ResourceHandleRepr {
self.inner
}
}