omaha_client/storage/
memory.rs

1// Copyright 2019 The Fuchsia Authors
2//
3// Licensed under a BSD-style license <LICENSE-BSD>, Apache License, Version 2.0
4// <LICENSE-APACHE or https://www.apache.org/licenses/LICENSE-2.0>, or the MIT
5// license <LICENSE-MIT or https://opensource.org/licenses/MIT>, at your option.
6// This file may not be copied, modified, or distributed except according to
7// those terms.
8
9use super::*;
10use futures::future::BoxFuture;
11use futures::prelude::*;
12use std::collections::HashMap;
13use thiserror::Error;
14
15/// The MemStorage struct is an in-memory-only implementation of the Storage trait, to be used in
16/// testing scenarios.
17#[derive(Debug)]
18pub struct MemStorage {
19    /// The values current stored.
20    data: HashMap<String, Value>,
21
22    /// Whether commit() has been called after set_*() or remove().
23    committed: bool,
24}
25
26/// Value is an enumeration for holding the values in MemStorage.
27#[derive(Debug)]
28enum Value {
29    String(String),
30    Int(i64),
31    Bool(bool),
32}
33
34/// The stub implementation doesn't return errors, so this is just a placeholder.
35#[derive(Debug, Error)]
36pub enum StorageErrors {
37    #[error("Unknown error occurred")]
38    Unknown,
39}
40
41impl MemStorage {
42    pub fn new() -> Self {
43        MemStorage {
44            data: HashMap::new(),
45            committed: true,
46        }
47    }
48
49    pub fn committed(&self) -> bool {
50        self.committed
51    }
52
53    pub fn len(&self) -> usize {
54        self.data.len()
55    }
56
57    pub fn is_empty(&self) -> bool {
58        self.data.is_empty()
59    }
60}
61
62impl Default for MemStorage {
63    fn default() -> Self {
64        Self::new()
65    }
66}
67
68impl Storage for MemStorage {
69    type Error = StorageErrors;
70
71    /// Get a string from the backing store.  Returns None if there is no value for the given key.
72    fn get_string<'a>(&'a self, key: &'a str) -> BoxFuture<'_, Option<String>> {
73        future::ready(match self.data.get(key) {
74            Some(Value::String(s)) => Some(s.clone()),
75            _ => None,
76        })
77        .boxed()
78    }
79
80    /// Get an int from the backing store.  Returns None if there is no value for the given key.
81    fn get_int<'a>(&'a self, key: &'a str) -> BoxFuture<'_, Option<i64>> {
82        future::ready(match self.data.get(key) {
83            Some(Value::Int(i)) => Some(*i),
84            _ => None,
85        })
86        .boxed()
87    }
88
89    /// Get a boolean from the backing store.  Returns None if there is no value for the given key.
90    fn get_bool<'a>(&'a self, key: &'a str) -> BoxFuture<'_, Option<bool>> {
91        future::ready(match self.data.get(key) {
92            Some(Value::Bool(b)) => Some(*b),
93            _ => None,
94        })
95        .boxed()
96    }
97
98    /// Set a value to be stored in the backing store.  The implementation should cache the value
99    /// until the |commit()| fn is called, and then persist all cached values at that time.
100    fn set_string<'a>(
101        &'a mut self,
102        key: &'a str,
103        value: &'a str,
104    ) -> BoxFuture<'_, Result<(), Self::Error>> {
105        self.data
106            .insert(key.to_string(), Value::String(value.to_string()));
107        self.committed = false;
108        future::ready(Ok(())).boxed()
109    }
110
111    /// Set a value to be stored in the backing store.  The implementation should cache the value
112    /// until the |commit()| fn is called, and then persist all cached values at that time.
113    fn set_int<'a>(
114        &'a mut self,
115        key: &'a str,
116        value: i64,
117    ) -> BoxFuture<'_, Result<(), Self::Error>> {
118        self.data.insert(key.to_string(), Value::Int(value));
119        self.committed = false;
120        future::ready(Ok(())).boxed()
121    }
122
123    /// Set a value to be stored in the backing store.  The implementation should cache the value
124    /// until the |commit()| fn is called, and then persist all cached values at that time.
125    fn set_bool<'a>(
126        &'a mut self,
127        key: &'a str,
128        value: bool,
129    ) -> BoxFuture<'_, Result<(), Self::Error>> {
130        self.data.insert(key.to_string(), Value::Bool(value));
131        self.committed = false;
132        future::ready(Ok(())).boxed()
133    }
134
135    fn remove<'a>(&'a mut self, key: &'a str) -> BoxFuture<'_, Result<(), Self::Error>> {
136        self.data.remove(key);
137        self.committed = false;
138        future::ready(Ok(())).boxed()
139    }
140
141    /// Persist all cached values to storage.
142    fn commit(&mut self) -> BoxFuture<'_, Result<(), Self::Error>> {
143        self.committed = true;
144        future::ready(Ok(())).boxed()
145    }
146}
147
148#[cfg(test)]
149mod tests {
150    use super::*;
151    use crate::storage::tests::*;
152    use futures::executor::block_on;
153
154    #[test]
155    fn test_set_get_remove_string() {
156        block_on(do_test_set_get_remove_string(&mut MemStorage::new()));
157    }
158
159    #[test]
160    fn test_set_get_remove_int() {
161        block_on(do_test_set_get_remove_int(&mut MemStorage::new()));
162    }
163
164    #[test]
165    fn test_set_option_int() {
166        block_on(do_test_set_option_int(&mut MemStorage::new()));
167    }
168
169    #[test]
170    fn test_set_get_remove_bool() {
171        block_on(do_test_set_get_remove_bool(&mut MemStorage::new()));
172    }
173
174    #[test]
175    fn test_set_get_remove_time() {
176        block_on(do_test_set_get_remove_time(&mut MemStorage::new()));
177    }
178
179    #[test]
180    fn test_return_none_for_wrong_value_type() {
181        block_on(do_return_none_for_wrong_value_type(&mut MemStorage::new()));
182    }
183
184    #[test]
185    fn test_ensure_no_error_remove_nonexistent_key() {
186        block_on(do_ensure_no_error_remove_nonexistent_key(
187            &mut MemStorage::new(),
188        ));
189    }
190
191    #[test]
192    fn test_committed() {
193        block_on(async {
194            let mut storage = MemStorage::new();
195            assert!(storage.committed());
196            storage.set_bool("some bool key", false).await.unwrap();
197            assert!(!storage.committed());
198            storage.commit().await.unwrap();
199            assert!(storage.committed());
200            storage
201                .set_string("some string key", "some string")
202                .await
203                .unwrap();
204            assert!(!storage.committed());
205            storage.set_int("some int key", 42).await.unwrap();
206            assert!(!storage.committed());
207            storage.commit().await.unwrap();
208            assert!(storage.committed());
209        });
210    }
211}