Skip to main content

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 { data: HashMap::new(), committed: true }
44    }
45
46    pub fn committed(&self) -> bool {
47        self.committed
48    }
49
50    pub fn len(&self) -> usize {
51        self.data.len()
52    }
53
54    pub fn is_empty(&self) -> bool {
55        self.data.is_empty()
56    }
57}
58
59impl Default for MemStorage {
60    fn default() -> Self {
61        Self::new()
62    }
63}
64
65impl Storage for MemStorage {
66    type Error = StorageErrors;
67
68    /// Get a string from the backing store.  Returns None if there is no value for the given key.
69    fn get_string<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<String>> {
70        future::ready(match self.data.get(key) {
71            Some(Value::String(s)) => Some(s.clone()),
72            _ => None,
73        })
74        .boxed()
75    }
76
77    /// Get an int from the backing store.  Returns None if there is no value for the given key.
78    fn get_int<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<i64>> {
79        future::ready(match self.data.get(key) {
80            Some(Value::Int(i)) => Some(*i),
81            _ => None,
82        })
83        .boxed()
84    }
85
86    /// Get a boolean from the backing store.  Returns None if there is no value for the given key.
87    fn get_bool<'a>(&'a self, key: &'a str) -> BoxFuture<'a, Option<bool>> {
88        future::ready(match self.data.get(key) {
89            Some(Value::Bool(b)) => Some(*b),
90            _ => None,
91        })
92        .boxed()
93    }
94
95    /// Set a value to be stored in the backing store.  The implementation should cache the value
96    /// until the |commit()| fn is called, and then persist all cached values at that time.
97    fn set_string<'a>(
98        &'a mut self,
99        key: &'a str,
100        value: &'a str,
101    ) -> BoxFuture<'a, Result<(), Self::Error>> {
102        self.data.insert(key.to_string(), Value::String(value.to_string()));
103        self.committed = false;
104        future::ready(Ok(())).boxed()
105    }
106
107    /// Set a value to be stored in the backing store.  The implementation should cache the value
108    /// until the |commit()| fn is called, and then persist all cached values at that time.
109    fn set_int<'a>(
110        &'a mut self,
111        key: &'a str,
112        value: i64,
113    ) -> BoxFuture<'a, Result<(), Self::Error>> {
114        self.data.insert(key.to_string(), Value::Int(value));
115        self.committed = false;
116        future::ready(Ok(())).boxed()
117    }
118
119    /// Set a value to be stored in the backing store.  The implementation should cache the value
120    /// until the |commit()| fn is called, and then persist all cached values at that time.
121    fn set_bool<'a>(
122        &'a mut self,
123        key: &'a str,
124        value: bool,
125    ) -> BoxFuture<'a, Result<(), Self::Error>> {
126        self.data.insert(key.to_string(), Value::Bool(value));
127        self.committed = false;
128        future::ready(Ok(())).boxed()
129    }
130
131    fn remove<'a>(&'a mut self, key: &'a str) -> BoxFuture<'a, Result<(), Self::Error>> {
132        self.data.remove(key);
133        self.committed = false;
134        future::ready(Ok(())).boxed()
135    }
136
137    /// Persist all cached values to storage.
138    fn commit(&mut self) -> BoxFuture<'_, Result<(), Self::Error>> {
139        self.committed = true;
140        future::ready(Ok(())).boxed()
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147    use crate::storage::tests::*;
148    use futures::executor::block_on;
149
150    #[test]
151    fn test_set_get_remove_string() {
152        block_on(do_test_set_get_remove_string(&mut MemStorage::new()));
153    }
154
155    #[test]
156    fn test_set_get_remove_int() {
157        block_on(do_test_set_get_remove_int(&mut MemStorage::new()));
158    }
159
160    #[test]
161    fn test_set_option_int() {
162        block_on(do_test_set_option_int(&mut MemStorage::new()));
163    }
164
165    #[test]
166    fn test_set_get_remove_bool() {
167        block_on(do_test_set_get_remove_bool(&mut MemStorage::new()));
168    }
169
170    #[test]
171    fn test_set_get_remove_time() {
172        block_on(do_test_set_get_remove_time(&mut MemStorage::new()));
173    }
174
175    #[test]
176    fn test_return_none_for_wrong_value_type() {
177        block_on(do_return_none_for_wrong_value_type(&mut MemStorage::new()));
178    }
179
180    #[test]
181    fn test_ensure_no_error_remove_nonexistent_key() {
182        block_on(do_ensure_no_error_remove_nonexistent_key(&mut MemStorage::new()));
183    }
184
185    #[test]
186    fn test_committed() {
187        block_on(async {
188            let mut storage = MemStorage::new();
189            assert!(storage.committed());
190            storage.set_bool("some bool key", false).await.unwrap();
191            assert!(!storage.committed());
192            storage.commit().await.unwrap();
193            assert!(storage.committed());
194            storage.set_string("some string key", "some string").await.unwrap();
195            assert!(!storage.committed());
196            storage.set_int("some int key", 42).await.unwrap();
197            assert!(!storage.committed());
198            storage.commit().await.unwrap();
199            assert!(storage.committed());
200        });
201    }
202}