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