junobuild_cdn/storage/state/
stable.rs1use 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::structures::collect_stable_vec;
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}