Skip to main content

Bundle

Struct Bundle 

Source
pub struct Bundle { /* private fields */ }
Expand description

The layers a game reads its files from.

Implementations§

Source§

impl Bundle

Source

pub fn fingerprint(&self) -> ContentHash

A fingerprint of the content that ships, the same on every platform: what a record or a save is stamped with (shuyi_core::Made). Any byte, name or file changed in a layer that ships changes it; the test layers never count, whatever the thread (story_only changes nothing).

The layers that ship are taken from the last read to the first (the base layer first), each file of a layer in path order, as its path’s length and bytes then its contents’ length and bytes, into the word-wise FNV-1a hash of the pack’s checksum. A layer that stands for a bundle (a pack’s) replaces what was taken so far with that bundle’s fingerprint. So a build that ships one pack has the fingerprint of the files it was baked from, and a layer laid over it counts as it would over them.

It reads every byte that ships, without telling the watcher: compute it once and keep it.

Source§

impl Bundle

Source

pub fn new(layers: Vec<Layer>) -> Bundle

The bundle of layers, read in the order given, except that the test layers are read after all the others.

Source

pub fn for_target( layers: Vec<Layer>, per_target: &[Layer], target: Option<&str>, ) -> Bundle

The bundle of layers for target (ADR-0008 §4): each of layers with its layer for target from per_target laid right above it, the one named <name>@<target> (target_layer_name), so a file there replaces the one at the same path. Every other layer of per_target (another target’s, or one over no layer of layers) is left out, and with no target all of them are: the bundle of layers alone (Bundle::new).

A game embeds its target layers beside its files, and picks the bundle by the target it bakes for (GameTools::bake) or runs on (PlatformFacts::target):

use shuyi_assets::bundle::{Bundle, Layer};

static ASSETS: &[(&str, &[u8])] = &[("hero.png", b"colour"), ("map.ron", b"map")];
static PLAYDATE: &[(&str, &[u8])] = &[("hero.png", b"drawn in black and white")];
let layers = || vec![Layer::table("assets", ASSETS)];
let targets = [Layer::table("assets@playdate", PLAYDATE)];

let bundle = Bundle::for_target(layers(), &targets, Some("playdate"));
assert_eq!(bundle.file("hero.png"), Some("drawn in black and white"));
assert_eq!(bundle.display_path("hero.png"), "assets@playdate/hero.png");
assert_eq!(bundle.file("map.ron"), Some("map"));
let web = Bundle::for_target(layers(), &targets, Some("web"));
assert_eq!(web.file("hero.png"), Some("colour"));
Source

pub fn layer_of(&self, path: &str) -> Option<&Layer>

The layer this thread reads the file at path from, if a layer has it: whether it is the game’s own, a mod’s, or a target’s (Layer::target).

Source

pub fn layers(&self) -> &[Layer]

Every layer, in the order files are read from them.

Source

pub fn file(&self, path: &str) -> Option<&'static str>

Returns the UTF-8 contents of the file at path (relative to a layer’s root, /-separated), or None if it is missing or not valid UTF-8.

Source

pub fn bytes(&self, path: &str) -> Option<&'static [u8]>

Returns the raw bytes of the file at path (relative to a layer’s root, /-separated), e.g. an image, or None if it is missing.

Source

pub fn has(&self, path: &str) -> bool

Whether a layer this thread reads has the file at path. Unlike Bundle::bytes, this reads nothing: the watcher isn’t told, and recording doesn’t hear of it.

Source

pub fn replace(&self, path: &str, bytes: Vec<u8>) -> bool

Puts bytes at path in the first layer handed in at run time that has the file, or the first of them if none has it, and says whether one took it (Layer::replace). A file of an embedded layer stays as it is, so a layer above one still wins over it.

Debug builds only, for a hot reload: the old bytes are leaked, so every &'static read of them stays valid.

use std::collections::BTreeMap;
use shuyi_assets::bundle::{Bundle, Layer};

let files = BTreeMap::from([("a.txt".to_owned(), b"old".to_vec())]);
let layer = Layer::owned("disk".to_owned(), files).unwrap();
let bundle = Bundle::new(vec![layer]);
let old = bundle.file("a.txt");
assert!(bundle.replace("a.txt", b"new".to_vec()));
assert_eq!(bundle.file("a.txt"), Some("new"));
assert_eq!(old, Some("old"));
Source

pub fn left_out(&self, path: &str) -> bool

Whether a layer this thread reads leaves the file at path out on purpose (Layer::leaving_out): a loader then does without it instead of reporting it missing.

Source

pub fn test_part(&self, path: &str) -> Option<(String, &'static str)>

The test layers’ own file at path, with its human-facing name, when a layer that ships has a file there too: the entries a data table gains in a build with the test files. Bundle::file reads the shipped one. None without test layers, inside story_only, and for a path only one of the two has.

Source

pub fn files_in(&self, dir: &str) -> Vec<&'static str>

Lists the paths (relative to a layer’s root, /-separated, sorted, each once) of every file directly inside directory dir, in any layer. A missing directory yields an empty list. Use "" for the root.

Source

pub fn files_under(&self, dir: &str) -> Vec<&'static str>

Lists the paths (relative to a layer’s root, /-separated, sorted, each once) of every file inside directory dir or any directory below it, in any layer. A missing directory yields an empty list. Use "" for the whole bundle.

Source

pub fn load_each<T, E>( &self, dir: &str, ext: &str, parse: impl FnMut(&LoadFile<'_>) -> Result<T, Vec<E>>, ) -> Result<BTreeMap<String, T>, Vec<E>>
where E: From<NotText>,

Parses every file of extension ext (without the dot) directly inside dir with parse, into a map by file name without the extension. A file that is not valid UTF-8 is an error of E (NotText); every broken file is reported, not only the first: the result is either the whole map or all the errors, in file order.

use shuyi_assets::bundle::{Bundle, Layer};
use shuyi_assets::ContentError;

static FILES: &[(&str, &[u8])] = &[("crops/a.txt", b"1"), ("crops/b.txt", b"2")];
let bundle = Bundle::new(vec![Layer::table("base", FILES)]);
let crops = bundle.load_each("crops", "txt", |file| {
    file.text
        .parse::<u32>()
        .map_err(|e| vec![ContentError::new(file.path, e.to_string())])
});
assert_eq!(crops.unwrap().get("b"), Some(&2));
§Errors

Every error parse returned, and a NotText for each file that is not UTF-8.

Source

pub fn display_path(&self, path: &str) -> String

The human-facing name of path: under the name of the layer it is read from (base/data/x.ron), or of the first layer that is neither private nor test files when no layer has it.

Source

pub fn is_private(&self, shown: &str) -> bool

Whether shown (a human-facing name, from Bundle::display_path) names a file of a private layer.

Trait Implementations§

Source§

impl Clone for Bundle

Source§

fn clone(&self) -> Bundle

Returns a duplicate of the value. Read more
1.0.0 (const: unstable) · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for Bundle

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result<(), Error>

Formats the value using the given formatter. Read more
Source§

impl Images for Bundle

The pictures of a one-bit target’s pack (shuyi bake --target playdate): the one-bit picture file at each image’s path, its first style, read where it lies. A PNG, or no file, is no picture.

Source§

fn image(&self, id: ImageId) -> Option<Image<'_>>

The picture of id, or None if there is none (the sprite is drawn as a grey rectangle).

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> DynClone for T
where T: Clone,

Source§

fn __clone_box(&self, _: Private) -> *mut ()

Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.