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.
89use super::*;
10use futures::future::BoxFuture;
11use futures::prelude::*;
12use std::collections::HashMap;
13use thiserror::Error;
1415/// 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.
20data: HashMap<String, Value>,
2122/// Whether commit() has been called after set_*() or remove().
23committed: bool,
24}
2526/// 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}
3334/// 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")]
38Unknown,
39}
4041impl MemStorage {
42pub fn new() -> Self {
43 MemStorage {
44 data: HashMap::new(),
45 committed: true,
46 }
47 }
4849pub fn committed(&self) -> bool {
50self.committed
51 }
5253pub fn len(&self) -> usize {
54self.data.len()
55 }
5657pub fn is_empty(&self) -> bool {
58self.data.is_empty()
59 }
60}
6162impl Default for MemStorage {
63fn default() -> Self {
64Self::new()
65 }
66}
6768impl Storage for MemStorage {
69type Error = StorageErrors;
7071/// Get a string from the backing store. Returns None if there is no value for the given key.
72fn get_string<'a>(&'a self, key: &'a str) -> BoxFuture<'_, Option<String>> {
73 future::ready(match self.data.get(key) {
74Some(Value::String(s)) => Some(s.clone()),
75_ => None,
76 })
77 .boxed()
78 }
7980/// Get an int from the backing store. Returns None if there is no value for the given key.
81fn get_int<'a>(&'a self, key: &'a str) -> BoxFuture<'_, Option<i64>> {
82 future::ready(match self.data.get(key) {
83Some(Value::Int(i)) => Some(*i),
84_ => None,
85 })
86 .boxed()
87 }
8889/// Get a boolean from the backing store. Returns None if there is no value for the given key.
90fn get_bool<'a>(&'a self, key: &'a str) -> BoxFuture<'_, Option<bool>> {
91 future::ready(match self.data.get(key) {
92Some(Value::Bool(b)) => Some(*b),
93_ => None,
94 })
95 .boxed()
96 }
9798/// 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.
100fn set_string<'a>(
101&'a mut self,
102 key: &'a str,
103 value: &'a str,
104 ) -> BoxFuture<'_, Result<(), Self::Error>> {
105self.data
106 .insert(key.to_string(), Value::String(value.to_string()));
107self.committed = false;
108 future::ready(Ok(())).boxed()
109 }
110111/// 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.
113fn set_int<'a>(
114&'a mut self,
115 key: &'a str,
116 value: i64,
117 ) -> BoxFuture<'_, Result<(), Self::Error>> {
118self.data.insert(key.to_string(), Value::Int(value));
119self.committed = false;
120 future::ready(Ok(())).boxed()
121 }
122123/// 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.
125fn set_bool<'a>(
126&'a mut self,
127 key: &'a str,
128 value: bool,
129 ) -> BoxFuture<'_, Result<(), Self::Error>> {
130self.data.insert(key.to_string(), Value::Bool(value));
131self.committed = false;
132 future::ready(Ok(())).boxed()
133 }
134135fn remove<'a>(&'a mut self, key: &'a str) -> BoxFuture<'_, Result<(), Self::Error>> {
136self.data.remove(key);
137self.committed = false;
138 future::ready(Ok(())).boxed()
139 }
140141/// Persist all cached values to storage.
142fn commit(&mut self) -> BoxFuture<'_, Result<(), Self::Error>> {
143self.committed = true;
144 future::ready(Ok(())).boxed()
145 }
146}
147148#[cfg(test)]
149mod tests {
150use super::*;
151use crate::storage::tests::*;
152use futures::executor::block_on;
153154#[test]
155fn test_set_get_remove_string() {
156 block_on(do_test_set_get_remove_string(&mut MemStorage::new()));
157 }
158159#[test]
160fn test_set_get_remove_int() {
161 block_on(do_test_set_get_remove_int(&mut MemStorage::new()));
162 }
163164#[test]
165fn test_set_option_int() {
166 block_on(do_test_set_option_int(&mut MemStorage::new()));
167 }
168169#[test]
170fn test_set_get_remove_bool() {
171 block_on(do_test_set_get_remove_bool(&mut MemStorage::new()));
172 }
173174#[test]
175fn test_set_get_remove_time() {
176 block_on(do_test_set_get_remove_time(&mut MemStorage::new()));
177 }
178179#[test]
180fn test_return_none_for_wrong_value_type() {
181 block_on(do_return_none_for_wrong_value_type(&mut MemStorage::new()));
182 }
183184#[test]
185fn test_ensure_no_error_remove_nonexistent_key() {
186 block_on(do_ensure_no_error_remove_nonexistent_key(
187&mut MemStorage::new(),
188 ));
189 }
190191#[test]
192fn test_committed() {
193 block_on(async {
194let mut storage = MemStorage::new();
195assert!(storage.committed());
196 storage.set_bool("some bool key", false).await.unwrap();
197assert!(!storage.committed());
198 storage.commit().await.unwrap();
199assert!(storage.committed());
200 storage
201 .set_string("some string key", "some string")
202 .await
203.unwrap();
204assert!(!storage.committed());
205 storage.set_int("some int key", 42).await.unwrap();
206assert!(!storage.committed());
207 storage.commit().await.unwrap();
208assert!(storage.committed());
209 });
210 }
211}