1use serde::{de::DeserializeOwned, ser::Serialize};
2use std::marker::PhantomData;
3
4use cosmwasm_std::{storage_keys::to_length_prefixed, to_json_vec, StdError, StdResult, Storage};
5
6use crate::type_helpers::{may_deserialize, must_deserialize};
7
8#[deprecated(
10 note = "The crate cosmwasm-storage is unmaintained and will be removed in CosmWasm 2.0. Please consider migrating to cw-storage-plus or simple cosmwasm-std storage calls."
11)]
12pub fn singleton<'a, T>(storage: &'a mut dyn Storage, key: &[u8]) -> Singleton<'a, T>
13where
14 T: Serialize + DeserializeOwned,
15{
16 Singleton::new(storage, key)
17}
18
19#[deprecated(
21 note = "The crate cosmwasm-storage is unmaintained and will be removed in CosmWasm 2.0. Please consider migrating to cw-storage-plus or simple cosmwasm-std storage calls."
22)]
23pub fn singleton_read<'a, T>(storage: &'a dyn Storage, key: &[u8]) -> ReadonlySingleton<'a, T>
24where
25 T: Serialize + DeserializeOwned,
26{
27 ReadonlySingleton::new(storage, key)
28}
29
30#[deprecated(
35 note = "The crate cosmwasm-storage is unmaintained and will be removed in CosmWasm 2.0. Please consider migrating to cw-storage-plus or simple cosmwasm-std storage calls."
36)]
37pub struct Singleton<'a, T>
38where
39 T: Serialize + DeserializeOwned,
40{
41 storage: &'a mut dyn Storage,
42 key: Vec<u8>,
43 data: PhantomData<T>,
45}
46
47impl<'a, T> Singleton<'a, T>
48where
49 T: Serialize + DeserializeOwned,
50{
51 pub fn new(storage: &'a mut dyn Storage, key: &[u8]) -> Self {
52 Singleton {
53 storage,
54 key: to_length_prefixed(key),
55 data: PhantomData,
56 }
57 }
58
59 pub fn save(&mut self, data: &T) -> StdResult<()> {
61 self.storage.set(&self.key, &to_json_vec(data)?);
62 Ok(())
63 }
64
65 pub fn remove(&mut self) {
66 self.storage.remove(&self.key)
67 }
68
69 pub fn load(&self) -> StdResult<T> {
71 let value = self.storage.get(&self.key);
72 must_deserialize(&value)
73 }
74
75 pub fn may_load(&self) -> StdResult<Option<T>> {
78 let value = self.storage.get(&self.key);
79 may_deserialize(&value)
80 }
81
82 pub fn update<A, E>(&mut self, action: A) -> Result<T, E>
87 where
88 A: FnOnce(T) -> Result<T, E>,
89 E: From<StdError>,
90 {
91 let input = self.load()?;
92 let output = action(input)?;
93 self.save(&output)?;
94 Ok(output)
95 }
96}
97
98#[deprecated(
101 note = "The crate cosmwasm-storage is unmaintained and will be removed in CosmWasm 2.0. Please consider migrating to cw-storage-plus or simple cosmwasm-std storage calls."
102)]
103pub struct ReadonlySingleton<'a, T>
104where
105 T: Serialize + DeserializeOwned,
106{
107 storage: &'a dyn Storage,
108 key: Vec<u8>,
109 data: PhantomData<T>,
111}
112
113impl<'a, T> ReadonlySingleton<'a, T>
114where
115 T: Serialize + DeserializeOwned,
116{
117 pub fn new(storage: &'a dyn Storage, key: &[u8]) -> Self {
118 ReadonlySingleton {
119 storage,
120 key: to_length_prefixed(key),
121 data: PhantomData,
122 }
123 }
124
125 pub fn load(&self) -> StdResult<T> {
127 let value = self.storage.get(&self.key);
128 must_deserialize(&value)
129 }
130
131 pub fn may_load(&self) -> StdResult<Option<T>> {
134 let value = self.storage.get(&self.key);
135 may_deserialize(&value)
136 }
137}
138
139#[cfg(test)]
140mod tests {
141 use super::*;
142 use cosmwasm_std::testing::MockStorage;
143 use serde::{Deserialize, Serialize};
144
145 use cosmwasm_std::{OverflowError, OverflowOperation, StdError};
146
147 #[derive(Serialize, Deserialize, PartialEq, Debug)]
148 struct Config {
149 pub owner: String,
150 pub max_tokens: i32,
151 }
152
153 #[test]
154 fn save_and_load() {
155 let mut store = MockStorage::new();
156 let mut single = Singleton::<Config>::new(&mut store, b"config");
157
158 assert!(single.load().is_err());
159 assert_eq!(single.may_load().unwrap(), None);
160
161 let cfg = Config {
162 owner: "admin".to_string(),
163 max_tokens: 1234,
164 };
165 single.save(&cfg).unwrap();
166
167 assert_eq!(cfg, single.load().unwrap());
168 }
169
170 #[test]
171 fn remove_works() {
172 let mut store = MockStorage::new();
173 let mut single = Singleton::<Config>::new(&mut store, b"config");
174
175 let cfg = Config {
177 owner: "admin".to_string(),
178 max_tokens: 1234,
179 };
180 single.save(&cfg).unwrap();
181 assert_eq!(cfg, single.load().unwrap());
182
183 single.remove();
185 assert_eq!(None, single.may_load().unwrap());
186
187 single.remove();
189 assert_eq!(None, single.may_load().unwrap());
190 }
191
192 #[test]
193 fn isolated_reads() {
194 let mut store = MockStorage::new();
195 let mut writer = singleton::<Config>(&mut store, b"config");
196
197 let cfg = Config {
198 owner: "admin".to_string(),
199 max_tokens: 1234,
200 };
201 writer.save(&cfg).unwrap();
202
203 let reader = singleton_read::<Config>(&store, b"config");
204 assert_eq!(cfg, reader.load().unwrap());
205
206 let other_reader = singleton_read::<Config>(&store, b"config2");
207 assert_eq!(other_reader.may_load().unwrap(), None);
208 }
209
210 #[test]
211 fn update_success() {
212 let mut store = MockStorage::new();
213 let mut writer = singleton::<Config>(&mut store, b"config");
214
215 let cfg = Config {
216 owner: "admin".to_string(),
217 max_tokens: 1234,
218 };
219 writer.save(&cfg).unwrap();
220
221 let output = writer.update(|mut c| -> StdResult<_> {
222 c.max_tokens *= 2;
223 Ok(c)
224 });
225 let expected = Config {
226 owner: "admin".to_string(),
227 max_tokens: 2468,
228 };
229 assert_eq!(output.unwrap(), expected);
230 assert_eq!(writer.load().unwrap(), expected);
231 }
232
233 #[test]
234 fn update_can_change_variable_from_outer_scope() {
235 let mut store = MockStorage::new();
236 let mut writer = singleton::<Config>(&mut store, b"config");
237 let cfg = Config {
238 owner: "admin".to_string(),
239 max_tokens: 1234,
240 };
241 writer.save(&cfg).unwrap();
242
243 let mut old_max_tokens = 0i32;
244 writer
245 .update(|mut c| -> StdResult<_> {
246 old_max_tokens = c.max_tokens;
247 c.max_tokens *= 2;
248 Ok(c)
249 })
250 .unwrap();
251 assert_eq!(old_max_tokens, 1234);
252 }
253
254 #[test]
255 fn update_does_not_change_data_on_error() {
256 let mut store = MockStorage::new();
257 let mut writer = singleton::<Config>(&mut store, b"config");
258
259 let cfg = Config {
260 owner: "admin".to_string(),
261 max_tokens: 1234,
262 };
263 writer.save(&cfg).unwrap();
264
265 let output = writer.update(|_c| {
266 Err(StdError::from(OverflowError::new(
267 OverflowOperation::Sub,
268 4,
269 7,
270 )))
271 });
272 match output.unwrap_err() {
273 StdError::Overflow { .. } => {}
274 err => panic!("Unexpected error: {err:?}"),
275 }
276 assert_eq!(writer.load().unwrap(), cfg);
277 }
278
279 #[test]
280 fn update_supports_custom_errors() {
281 #[derive(Debug)]
282 enum MyError {
283 Std(StdError),
284 Foo,
285 }
286
287 impl From<StdError> for MyError {
288 fn from(original: StdError) -> MyError {
289 MyError::Std(original)
290 }
291 }
292
293 let mut store = MockStorage::new();
294 let mut writer = singleton::<Config>(&mut store, b"config");
295
296 let cfg = Config {
297 owner: "admin".to_string(),
298 max_tokens: 1234,
299 };
300 writer.save(&cfg).unwrap();
301
302 let res = writer.update(|mut c| {
303 if c.max_tokens > 5000 {
304 return Err(MyError::Foo);
305 }
306 if c.max_tokens > 20 {
307 return Err(StdError::generic_err("broken stuff").into()); }
309 if c.max_tokens > 10 {
310 to_json_vec(&c)?; }
312 c.max_tokens += 20;
313 Ok(c)
314 });
315 match res.unwrap_err() {
316 MyError::Std(StdError::GenericErr { .. }) => {}
317 err => panic!("Unexpected error: {err:?}"),
318 }
319 assert_eq!(writer.load().unwrap(), cfg);
320 }
321}