junobuild_collections/assert/
rules.rs

1use crate::assert::collection::{is_not_system_collection, is_system_collection};
2use crate::constants::core::SYS_COLLECTION_PREFIX;
3use crate::errors::{
4    JUNO_COLLECTIONS_ERROR_DELETE_PREFIX_RESERVED,
5    JUNO_COLLECTIONS_ERROR_MODIFY_RESERVED_COLLECTION, JUNO_COLLECTIONS_ERROR_PREFIX_RESERVED,
6    JUNO_COLLECTIONS_ERROR_RATE_CONFIG_ENABLED, JUNO_COLLECTIONS_ERROR_RESERVED_NAME,
7};
8use crate::types::core::CollectionKey;
9use crate::types::interface::SetRule;
10use crate::types::rules::{Memory, Rule, Rules};
11use junobuild_shared::assert::assert_version;
12use junobuild_shared::types::state::Version;
13
14pub fn assert_memory(current_rule: Option<&Rule>, memory: &Option<Memory>) -> Result<(), String> {
15    // Validate memory type does not change
16    match current_rule {
17        None => (),
18        Some(current_rule) => match memory {
19            None => {
20                return Err("The type of memory must be provided.".to_string());
21            }
22            Some(Memory::Heap) => {
23                if !matches!(&current_rule.mem(), Memory::Heap) {
24                    return Err("The type of memory cannot be modified to heap.".to_string());
25                }
26            }
27            Some(Memory::Stable) => {
28                if !matches!(&current_rule.mem(), Memory::Stable) {
29                    return Err("The type of memory cannot be modified to stable.".to_string());
30                }
31            }
32        },
33    }
34
35    Ok(())
36}
37
38pub fn assert_mutable_permissions(
39    current_rule: Option<&Rule>,
40    user_rule: &SetRule,
41) -> Result<(), String> {
42    // Validate mutability does not change
43    match current_rule {
44        None => (),
45        Some(current_rule) => match user_rule.mutable_permissions {
46            None => {
47                return Err("The immutable permissions information must be provided.".to_string());
48            }
49            Some(mutable_permissions) => {
50                let current_permissions = current_rule.mutable_permissions.unwrap_or(true);
51
52                if current_permissions != mutable_permissions && !current_permissions {
53                    return Err("The immutable permissions cannot be made mutable.".to_string());
54                }
55            }
56        },
57    }
58
59    // Validate permissions do not change
60    match current_rule {
61        None => (),
62        Some(current_rule) => {
63            if current_rule.write != user_rule.write
64                && !current_rule.mutable_permissions.unwrap_or(true)
65            {
66                return Err("The write permission is immutable.".to_string());
67            }
68
69            if current_rule.read != user_rule.read
70                && !current_rule.mutable_permissions.unwrap_or(true)
71            {
72                return Err("The read permission is immutable.".to_string());
73            }
74        }
75    }
76
77    Ok(())
78}
79
80pub fn assert_write_version(
81    current_rule: Option<&Rule>,
82    version: &Option<Version>,
83) -> Result<(), String> {
84    // Validate timestamp
85    match current_rule {
86        None => (),
87        Some(current_rule) => match assert_version(*version, current_rule.version) {
88            Ok(_) => (),
89            Err(e) => {
90                return Err(e);
91            }
92        },
93    }
94
95    Ok(())
96}
97
98pub fn assert_system_collection_set_permission(
99    collection: &CollectionKey,
100    current_rule: Option<&Rule>,
101    user_rule: &SetRule,
102) -> Result<(), String> {
103    // Allow non-system collections to proceed without restrictions
104    if is_not_system_collection(collection) {
105        return Ok(());
106    }
107
108    // System collections cannot be created with a setter call but can be edited under certain circumstances.
109    let current_rule =
110        current_rule.ok_or_else(|| JUNO_COLLECTIONS_ERROR_PREFIX_RESERVED.to_string())?;
111
112    if current_rule.read != user_rule.read
113        || current_rule.write != user_rule.write
114        || current_rule.memory != user_rule.memory
115        || current_rule.mutable_permissions != user_rule.mutable_permissions
116        || current_rule.max_size != user_rule.max_size
117        || current_rule.max_capacity != user_rule.max_capacity
118    {
119        return Err(format!(
120            "{JUNO_COLLECTIONS_ERROR_MODIFY_RESERVED_COLLECTION} ({collection})"
121        ));
122    }
123
124    if current_rule.rate_config.is_some() && user_rule.rate_config.is_none() {
125        return Err(JUNO_COLLECTIONS_ERROR_RATE_CONFIG_ENABLED.to_string());
126    }
127
128    Ok(())
129}
130
131pub fn assert_system_collection_delete_permission(
132    collection: &CollectionKey,
133) -> Result<(), String> {
134    if is_system_collection(collection) {
135        return Err(format!(
136            "{JUNO_COLLECTIONS_ERROR_DELETE_PREFIX_RESERVED} ({SYS_COLLECTION_PREFIX})"
137        ));
138    }
139
140    Ok(())
141}
142
143// In the storage, the collection name must be included within the path (e.g., /hello/index.html for the collection "hello").
144// Therefore, to avoid conflicts with system collections like #dapp and #releases (used in the Console),
145// we need to ensure that the custom collection name does not clash with any reserved system collection names.
146pub fn assert_storage_reserved_collection(
147    collection: &CollectionKey,
148    rules: &Rules,
149) -> Result<(), String> {
150    // We do not have to check system collection.
151    if is_system_collection(collection) {
152        return Ok(());
153    }
154
155    let reserved_collection = format!("{SYS_COLLECTION_PREFIX}{collection}");
156
157    if rules.contains_key(&reserved_collection) {
158        return Err(JUNO_COLLECTIONS_ERROR_RESERVED_NAME.to_string());
159    }
160
161    Ok(())
162}