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