1use std::collections::BTreeMap;
4use std::path::{Path, PathBuf};
5
6use phoxal_model::AssetId;
7use phoxal_model::manifest::ManifestDocument;
8
9use crate::{
10 ASSETS_DIR, BIN_DIR, BundleError, BundlePath, BundleRoot, MANIFEST_FILE, ParticipantAssets,
11 RuntimeBundle, copy_executable_source, create_staging_root, ensure_staging_directory,
12 prepare_publish_parent, publish_staging_root, reject_existing_target, write_new_file,
13};
14
15pub struct BundleWriter;
17
18impl BundleWriter {
19 pub fn write(
38 root: impl AsRef<Path>,
39 manifest: &ManifestDocument,
40 assets: &BTreeMap<AssetId, Vec<u8>>,
41 binaries: &BTreeMap<BundlePath, PathBuf>,
42 ) -> Result<RuntimeBundle, BundleError> {
43 let publish_target = prepare_publish_parent(root.as_ref())?;
44 reject_existing_target(&publish_target)?;
45 let staging_path = create_staging_root(&publish_target)?;
46 let staged = BundleRoot::open(&staging_path)?;
47 let written = stage(&staged, manifest, assets, binaries);
48 let bundle = match written {
49 Ok(bundle) => bundle,
50 Err(error) => {
51 let _ = std::fs::remove_dir_all(&staging_path);
52 return Err(error);
53 }
54 };
55 if let Err(error) = publish_staging_root(staged.path(), &publish_target) {
56 let _ = std::fs::remove_dir_all(&staging_path);
57 return Err(error);
58 }
59 Ok(bundle.relocated(publish_target))
60 }
61}
62
63fn stage(
64 root: &BundleRoot,
65 manifest: &ManifestDocument,
66 assets: &BTreeMap<AssetId, Vec<u8>>,
67 binaries: &BTreeMap<BundlePath, PathBuf>,
68) -> Result<RuntimeBundle, BundleError> {
69 ensure_staging_directory(root, ASSETS_DIR)?;
70 ensure_staging_directory(root, BIN_DIR)?;
71 for (id, bytes) in assets {
72 write_new_file(root, &ParticipantAssets::path(id)?, bytes)?;
73 }
74 for (destination, source) in binaries {
75 copy_executable_source(root, source, destination)?;
76 }
77 let json = serde_json::to_vec_pretty(manifest)?;
78 write_new_file(root, &BundlePath::new(MANIFEST_FILE)?, &json)?;
79 RuntimeBundle::open(root.path())
80}