canister_tools/
lib.rs

1//! A Rust library for canisters on the [internet-computer](https://internetcomputer.org). 
2//! 
3//! Features
4//! - Easy simple upgrade strategy.
5//! - Safe data snapshots management manual upload and download of the canister data. 
6//! 
7//! Each global data structure is set and registered with a [MemoryId] in the canister_init hook and in the canister_post_upgrade hook. 
8//! On an upgrade, the library will serialize the registered data structures into the 
9//! corresponding [MemoryId].
10//!
11//! For a global data type to be serializable, it must implement the [Serializable] trait.
12//! Types that implement candid's `CandidType` and `Deserialize` traits auto-implement the [Serializable] trait with the [candid](https://docs.rs/candid/latest/candid/index.html) binary serialization format. 
13//!
14//! 
15//! For the safety and to make sure that your data is always accessible even if something goes wrong in the 
16//! pre_upgrade hook or elsewhere, this library creates canister methods that can be used in those cases 
17//! to download and upload the canister data. 
18//! 
19//! 
20//! This library creates the following canister methods for the state-snapshot management and stable-memory management. 
21//! ```candid    
22//! type MemoryId = nat8;
23//! type Offset = nat64;
24//! type Length = nat64;
25//! type StateSnapshotLength = nat64;
26//! type WasmPages = nat64;
27//! 
28//! service : {
29//!     // Takes a snapshot of the data structure registered at the given MemoryId.
30//!     controller_create_state_snapshot : (MemoryId) -> (StateSnapshotLength);
31//!     
32//!     // Download the snapshot of the data corresponding to the given MemoryId.
33//!     // Download the data in chunks.
34//!     controller_download_state_snapshot : (MemoryId, Offset, Length) -> (blob) query;
35//!     
36//!     // Clears the snapshot of the data corresponding to the given MemoryId.
37//!     // When uploading data onto the data structure, call this method first to clear
38//!     // the snapshot before uploading a customized snapshot.
39//!     controller_clear_state_snapshot : (MemoryId) -> ();
40//!     
41//!     // Upload the serialized data structure for the given MemoryId in chunks that can then be deserialized and loaded onto the canister global variable.   
42//!     controller_append_state_snapshot : (MemoryId, blob) -> ();
43//!     
44//!     // Deserializes the snapshot for the data structure corresponding to the given MemoryId
45//!     // and loads it onto the canister's global variable.
46//!     controller_load_state_snapshot : (MemoryId) -> ();
47//! 
48//!
49//!
50//!     // Common stable memory functions as canister methods.
51//!     // Useful when using a custom stable-memory strategy for one or some of the MemoryIds. 
52//!     controller_stable_memory_read : (MemoryId, Offset, Length) -> (blob) query;
53//!     controller_stable_memory_write : (MemoryId, Offset, blob) -> ();
54//!     controller_stable_memory_size : (MemoryId) -> (nat64) query;
55//!     controller_stable_memory_grow : (MemoryId, WasmPages) -> (int64);
56//! }
57//! ```
58//! 
59//! 
60//! # Sample
61//! 
62//! ```rust
63//! use core::cell::RefCell;
64//! use ic_cdk::{
65//!     update,
66//!     query,    
67//!     init,
68//!     pre_upgrade,
69//!     post_upgrade
70//! };
71//! use candid::{CandidType, Deserialize};
72//! use canister_tools::{
73//!     MemoryId,
74//!     localkey::refcell::{with, with_mut},
75//! };
76//! 
77//! 
78//! 
79//! const DATA_UPGRADE_SERIALIZATION_MEMORY_ID: MemoryId = MemoryId::new(0);
80//! 
81//! 
82//! #[derive(CandidType, Deserialize)]
83//! struct Data {
84//!     field_one: String,
85//!     field_two: u64,
86//! }
87//! 
88//! 
89//! thread_local! {
90//!     static DATA: RefCell<Data> = RefCell::new(
91//!         Data{
92//!             field_one: String::from("Hi World"),
93//!             field_two: 55
94//!         }
95//!     );
96//! }
97//! 
98//! #[init]
99//! fn init() {
100//!     canister_tools::init(&DATA, DATA_UPGRADE_SERIALIZATION_MEMORY_ID);
101//! }
102//! 
103//! #[pre_upgrade]
104//! fn pre_upgrade() {
105//!     canister_tools::pre_upgrade();
106//! }
107//! 
108//! #[post_upgrade]
109//! fn post_upgrade() {
110//!     canister_tools::post_upgrade(&DATA, DATA_UPGRADE_SERIALIZATION_MEMORY_ID, None::<fn(Data) -> Data>);
111//! }
112//! 
113//! 
114//! 
115//! #[query]
116//! pub fn get_field_two() -> u64 {
117//!     with(&DATA, |data| {
118//!         data.field_two
119//!     })
120//! }
121//! 
122//! #[update]
123//! pub fn set_field_two(value: u64) {
124//!     with_mut(&DATA, |data| {
125//!         data.field_two = value;
126//!     });
127//! }
128//! ```
129//! 
130//! 
131//! 
132//! 
133//! 
134//! 
135//! 
136
137
138
139
140mod stable_memory_tools;
141pub use stable_memory_tools::*;
142
143pub mod localkey {
144    pub mod refcell {
145        use std::{
146            cell::RefCell,
147            thread::LocalKey,
148        };
149        /// Function for a RefCell defined in a thread_local!{}, gives direct immutable access to the data structure within the RefCell.
150        /// 
151        /// ## Sample
152        /// ```
153        /// struct House {
154        ///     color: String,
155        ///     size: u32
156        /// }
157        /// thread_local!{
158        ///     static HOUSE: RefCell<House> = 
159        ///         RefCell::new(
160        ///             House{
161        ///                 color: "blue".to_string(),
162        ///                 size: 5000
163        ///             }
164        ///         );
165        /// } 
166        /// 
167        /// fn house_size() -> u32 {
168        ///     with(&HOUSE, |house| {
169        ///         house.size
170        ///     })
171        /// }
172        /// ```        
173        pub fn with<T: 'static, R, F>(s: &'static LocalKey<RefCell<T>>, f: F) -> R
174        where 
175            F: FnOnce(&T) -> R 
176        {
177            s.with(|b| {
178                f(&*b.borrow())
179            })
180        }
181        /// Function for a RefCell defined in a thread_local!{}, gives direct mutable access to the data structure within the RefCell.        
182        /// 
183        /// ## Sample
184        /// ```
185        /// struct House {
186        ///     color: String,
187        ///     size: u32
188        /// }
189        /// thread_local!{
190        ///     static HOUSE: RefCell<House> = 
191        ///         RefCell::new(
192        ///             House{
193        ///                 color: "blue".to_string(),
194        ///                 size: 5000
195        ///             }
196        ///         );
197        /// } 
198        /// 
199        /// fn change_house_size(new_size: u32) {
200        ///     with_mut(&HOUSE, |house| {
201        ///         house.size = new_size;
202        ///     });
203        /// }
204        /// ```        
205        pub fn with_mut<T: 'static, R, F>(s: &'static LocalKey<RefCell<T>>, f: F) -> R
206        where 
207            F: FnOnce(&mut T) -> R 
208        {
209            s.with(|b| {
210                f(&mut *b.borrow_mut())
211            })
212        }
213    }
214    pub mod cell {
215        use std::{
216            cell::Cell,
217            thread::LocalKey
218        };
219        /// Function for a Cell defined in a thread_local!{}, get the value within the Cell.
220        ///
221        /// ## Sample
222        /// ```
223        /// thread_local!{
224        ///     static VALUE: Cell<u64> = Cell::new(5);
225        /// } 
226        /// 
227        /// fn multiply_global_value(multiply_by: u64) -> u64 {
228        ///     get(&VALUE) * multiply_by
229        /// }
230        /// ```        
231        pub fn get<T: 'static + Copy>(s: &'static LocalKey<Cell<T>>) -> T {
232            s.with(|c| { c.get() })
233        }
234        /// Function for a Cell defined in a thread_local!{}, sets the value within the Cell.
235        /// 
236        /// ## Sample
237        /// ```
238        /// thread_local!{
239        ///     static VALUE: Cell<u64> = Cell::new(5);
240        /// } 
241        /// 
242        /// fn set_global_value(new_value: u64) {
243        ///     set(&VALUE, new_value);
244        /// }
245        /// ```        
246        pub fn set<T: 'static + Copy>(s: &'static LocalKey<Cell<T>>, v: T) {
247            s.with(|c| { c.set(v); });
248        }
249    }
250}
251
252