Skip to main content

junobuild_cdn/storage/state/
stable.rs

1use crate::proposals::ProposalId;
2use crate::storage::state::types::{ProposalAssetKey, ProposalAssetsStable};
3use crate::storage::{ProposalContentChunkKey, ProposalContentChunksStable};
4use crate::strategies::CdnStableStrategy;
5use junobuild_collections::types::core::CollectionKey;
6use junobuild_shared::data::collect::collect_stable_vec;
7use junobuild_shared::memory::serializers::deserialize_from_bytes;
8use junobuild_shared::types::core::Blob;
9use junobuild_storage::stable_utils::insert_asset_encoding_stable;
10use junobuild_storage::types::state::FullPath;
11use junobuild_storage::types::store::{Asset, AssetEncoding, BlobOrKey};
12use std::borrow::Cow;
13use std::ops::RangeBounds;
14
15pub fn get_asset(
16    cdn_stable: &impl CdnStableStrategy,
17    proposal_id: &ProposalId,
18    collection: &CollectionKey,
19    full_path: &FullPath,
20) -> Option<Asset> {
21    cdn_stable.with_assets(|assets| get_asset_impl(proposal_id, collection, full_path, assets))
22}
23
24fn get_asset_impl(
25    proposal_id: &ProposalId,
26    collection: &CollectionKey,
27    full_path: &FullPath,
28    assets: &ProposalAssetsStable,
29) -> Option<Asset> {
30    assets.get(&proposal_asset_key(proposal_id, collection, full_path))
31}
32
33pub fn get_content_chunks(
34    cdn_stable: &impl CdnStableStrategy,
35    encoding: &AssetEncoding,
36    chunk_index: usize,
37) -> Option<Blob> {
38    cdn_stable.with_content_chunks(|content_chunks| {
39        get_content_chunks_impl(encoding, chunk_index, content_chunks)
40    })
41}
42
43fn get_content_chunks_impl(
44    encoding: &AssetEncoding,
45    chunk_index: usize,
46    content_chunks: &ProposalContentChunksStable,
47) -> Option<Blob> {
48    let key: ProposalContentChunkKey =
49        deserialize_from_bytes(Cow::Owned(encoding.content_chunks[chunk_index].clone()));
50    content_chunks.get(&key)
51}
52
53pub fn get_assets(
54    cdn_stable: &impl CdnStableStrategy,
55    proposal_id: &ProposalId,
56) -> Vec<(ProposalAssetKey, Asset)> {
57    cdn_stable.with_assets(|assets| get_assets_impl(proposal_id, assets))
58}
59
60fn get_assets_impl(
61    proposal_id: &ProposalId,
62    proposal_assets: &ProposalAssetsStable,
63) -> Vec<(ProposalAssetKey, Asset)> {
64    collect_stable_vec(proposal_assets.range(filter_assets_range(proposal_id)))
65}
66
67fn filter_assets_range(proposal_id: &ProposalId) -> impl RangeBounds<ProposalAssetKey> {
68    let start_key = ProposalAssetKey {
69        proposal_id: *proposal_id,
70        collection: "".to_string(),
71        full_path: "".to_string(),
72    };
73
74    let end_key = ProposalAssetKey {
75        proposal_id: *proposal_id + 1,
76        collection: "".to_string(),
77        full_path: "".to_string(),
78    };
79
80    start_key..end_key
81}
82
83pub fn insert_asset_encoding(
84    cdn_stable: &impl CdnStableStrategy,
85    proposal_id: &ProposalId,
86    full_path: &FullPath,
87    encoding_type: &str,
88    encoding: &AssetEncoding,
89    asset: &mut Asset,
90) {
91    let stable_key_fn = {
92        let proposal_id = *proposal_id;
93
94        move |full_path: &FullPath, encoding_type: &str, chunk_index: usize| {
95            proposal_encoding_chunk_key(&proposal_id, full_path, encoding_type, chunk_index)
96        }
97    };
98
99    cdn_stable.with_content_chunks_mut(|content_chunks| {
100        insert_asset_encoding_stable(
101            full_path,
102            encoding_type,
103            encoding,
104            asset,
105            stable_key_fn,
106            content_chunks,
107        )
108    })
109}
110
111pub fn insert_asset(
112    cdn_stable: &impl CdnStableStrategy,
113    proposal_id: &ProposalId,
114    collection: &CollectionKey,
115    full_path: &FullPath,
116    asset: &Asset,
117) {
118    cdn_stable.with_assets_mut(|assets| {
119        insert_asset_impl(proposal_id, collection, full_path, asset, assets)
120    })
121}
122
123fn insert_asset_impl(
124    proposal_id: &ProposalId,
125    collection: &CollectionKey,
126    full_path: &FullPath,
127    asset: &Asset,
128    assets: &mut ProposalAssetsStable,
129) {
130    assets.insert(
131        proposal_asset_key(proposal_id, collection, full_path),
132        asset.clone(),
133    );
134}
135
136pub fn delete_asset(cdn_stable: &impl CdnStableStrategy, key: &ProposalAssetKey) -> Option<Asset> {
137    cdn_stable.with_assets_mut(|assets| delete_asset_impl(key, assets))
138}
139
140fn delete_asset_impl(key: &ProposalAssetKey, assets: &mut ProposalAssetsStable) -> Option<Asset> {
141    assets.remove(key)
142}
143
144pub fn delete_content_chunks(
145    cdn_stable: &impl CdnStableStrategy,
146    content_chunks_keys: &[BlobOrKey],
147) {
148    cdn_stable.with_content_chunks_mut(|content_chunks| {
149        delete_content_chunks_impl(content_chunks_keys, content_chunks)
150    })
151}
152
153fn delete_content_chunks_impl(
154    content_chunks_keys: &[BlobOrKey],
155    content_chunks: &mut ProposalContentChunksStable,
156) {
157    for chunk in content_chunks_keys.iter() {
158        let key: ProposalContentChunkKey = deserialize_from_bytes(Cow::Owned(chunk.clone()));
159        content_chunks.remove(&key);
160    }
161}
162
163fn proposal_asset_key(
164    proposal_id: &ProposalId,
165    collection: &CollectionKey,
166    full_path: &FullPath,
167) -> ProposalAssetKey {
168    ProposalAssetKey {
169        proposal_id: *proposal_id,
170        collection: collection.clone(),
171        full_path: full_path.clone(),
172    }
173}
174
175fn proposal_encoding_chunk_key(
176    proposal_id: &ProposalId,
177    full_path: &FullPath,
178    encoding_type: &str,
179    chunk_index: usize,
180) -> ProposalContentChunkKey {
181    ProposalContentChunkKey {
182        proposal_id: *proposal_id,
183        full_path: full_path.clone(),
184        encoding_type: encoding_type.to_owned(),
185        chunk_index,
186    }
187}