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