use concinnity_cook::blob::BlobLock;
fn recorded_names(lock: &BlobLock) -> Vec<(u32, String)> {
lock.assets
.iter()
.filter_map(|a| a.id.map(|id| (id, a.name.clone())))
.chain(
lock.resources
.iter()
.filter_map(|r| r.id.map(|id| (id, r.name.clone()))),
)
.collect()
}
pub(crate) fn prime_from_lock_file() -> std::io::Result<usize> {
let content = std::fs::read_to_string(concinnity_cook::blob::LOCK_PATH)?;
let lock: BlobLock = serde_json::from_str(&content)
.map_err(|e| std::io::Error::new(std::io::ErrorKind::InvalidData, e.to_string()))?;
let pairs = recorded_names(&lock);
if pairs.is_empty() || !crate::ecs::asset_id::prime_name_table(&pairs) {
return Ok(0);
}
Ok(pairs.len())
}
#[cfg(test)]
mod tests {
use super::*;
use crate::ecs::asset_id;
fn lock_with(assets: &[(&str, Option<u32>)], resources: &[(&str, Option<u32>)]) -> BlobLock {
let assets = assets
.iter()
.map(|(name, id)| {
serde_json::json!({
"name": name, "id": id, "kind": "Component",
"discriminant": 0, "args_hash": "", "payload_blob": null
})
})
.collect::<Vec<_>>();
let resources = resources
.iter()
.map(|(name, id)| {
serde_json::json!({
"name": name, "id": id, "kind": "Texture",
"handle": 0, "args_hash": "", "payload_blob": null
})
})
.collect::<Vec<_>>();
serde_json::from_value(serde_json::json!({
"engine_version": "0", "built_at": "", "blobs": [],
"assets": assets, "resources": resources, "injected": []
}))
.unwrap()
}
#[test]
fn recorded_names_prime_the_interner_at_their_baked_ids() {
asset_id::reset_interner();
let lock = lock_with(
&[("cam", Some(0)), ("floor", Some(2))],
&[("brick", Some(1))],
);
let pairs = recorded_names(&lock);
assert!(asset_id::prime_name_table(&pairs));
assert_eq!(asset_id::intern("cam"), asset_id::AssetId(0));
assert_eq!(asset_id::intern("brick"), asset_id::AssetId(1));
assert_eq!(asset_id::intern("floor"), asset_id::AssetId(2));
}
#[test]
fn a_lock_without_ids_is_ignored() {
let lock = lock_with(&[("cam", None)], &[("brick", None)]);
assert!(recorded_names(&lock).is_empty());
}
#[test]
fn priming_from_a_written_lock_installs_every_recorded_name() {
let _cwd = crate::test_support::lock_in_temp_cwd();
asset_id::reset_interner();
let lock = lock_with(
&[("cam", Some(0)), ("floor", Some(2))],
&[("brick", Some(1))],
);
std::fs::write(
concinnity_cook::blob::LOCK_PATH,
serde_json::to_string(&lock).unwrap(),
)
.unwrap();
assert_eq!(prime_from_lock_file().unwrap(), 3);
assert_eq!(asset_id::intern("cam"), asset_id::AssetId(0));
assert_eq!(asset_id::intern("brick"), asset_id::AssetId(1));
assert_eq!(asset_id::intern("floor"), asset_id::AssetId(2));
}
#[test]
fn priming_from_an_id_less_lock_installs_nothing() {
let _cwd = crate::test_support::lock_in_temp_cwd();
asset_id::reset_interner();
let lock = lock_with(&[("cam", None)], &[]);
std::fs::write(
concinnity_cook::blob::LOCK_PATH,
serde_json::to_string(&lock).unwrap(),
)
.unwrap();
assert_eq!(prime_from_lock_file().unwrap(), 0);
}
#[test]
fn priming_over_a_filled_table_installs_nothing() {
let _cwd = crate::test_support::lock_in_temp_cwd();
asset_id::reset_interner();
asset_id::intern("already_here");
let lock = lock_with(&[("cam", Some(0))], &[]);
std::fs::write(
concinnity_cook::blob::LOCK_PATH,
serde_json::to_string(&lock).unwrap(),
)
.unwrap();
assert_eq!(prime_from_lock_file().unwrap(), 0);
assert_eq!(asset_id::intern("already_here"), asset_id::AssetId(0));
}
#[test]
fn a_missing_or_corrupt_lock_is_an_error() {
let _cwd = crate::test_support::lock_in_temp_cwd();
let missing = prime_from_lock_file().expect_err("no lock in a fresh dir");
assert_eq!(missing.kind(), std::io::ErrorKind::NotFound);
std::fs::write(concinnity_cook::blob::LOCK_PATH, "{ not json").unwrap();
let corrupt = prime_from_lock_file().expect_err("a corrupt lock");
assert_eq!(corrupt.kind(), std::io::ErrorKind::InvalidData);
}
}