Bug 1885565 - Part 1: Add mozac_ic_avatar_circle_24 to ui-icons r=android-reviewers...
[gecko.git] / toolkit / components / kvstore / kvstore.sys.mjs
blob9085eed530c5833916259978cb485237525b3145
1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
5 const gKeyValueService = Cc["@mozilla.org/key-value-service;1"].getService(
6   Ci.nsIKeyValueService
7 );
9 function promisify(fn, ...args) {
10   return new Promise((resolve, reject) => {
11     fn({ resolve, reject }, ...args);
12   });
15 /**
16  * This module wraps the nsIKeyValue* interfaces in a Promise-based API.
17  * To use it, import it, then call the KeyValueService.getOrCreate() method
18  * with a database's path and (optionally) its name:
19  *
20  * ```
21  *     let { keyValueService } =
22  *       ChromeUtils.importESModule("resource://gre/modules/kvstore.sys.mjs");
23  *     let database = await KeyValueService.getOrCreate(path, name);
24  * ```
25  *
26  * See the documentation in nsIKeyValue.idl for more information about the API
27  * for key/value storage.
28  */
30 export class KeyValueService {
31   static RecoveryStrategy = {
32     ERROR: gKeyValueService.ERROR,
33     DISCARD: gKeyValueService.DISCARD,
34     RENAME: gKeyValueService.RENAME,
35   };
37   static async getOrCreate(dir, name) {
38     return new KeyValueDatabase(
39       await promisify(gKeyValueService.getOrCreate, dir, name)
40     );
41   }
43   static async getOrCreateWithOptions(
44     dir,
45     name,
46     { strategy = gKeyValueService.RENAME } = {}
47   ) {
48     return new KeyValueDatabase(
49       await promisify(
50         gKeyValueService.getOrCreateWithOptions,
51         dir,
52         name,
53         strategy
54       )
55     );
56   }
59 /**
60  * A class that wraps an nsIKeyValueDatabase in a Promise-based API.
61  *
62  * This class isn't exported, so you can't instantiate it directly, but you
63  * can retrieve an instance of this class via KeyValueService.getOrCreate():
64  *
65  * ```
66  *     const database = await KeyValueService.getOrCreate(path, name);
67  * ```
68  *
69  * You can then call its put(), get(), has(), and delete() methods to access
70  * and manipulate key/value pairs:
71  *
72  * ```
73  *     await database.put("foo", 1);
74  *     await database.get("foo") === 1; // true
75  *     await database.has("foo"); // true
76  *     await database.delete("foo");
77  *     await database.has("foo"); // false
78  * ```
79  *
80  * You can also call writeMany() to put/delete multiple key/value pairs:
81  *
82  * ```
83  *     await database.writeMany({
84  *       key1: "value1",
85  *       key2: "value2",
86  *       key3: "value3",
87  *       key4: null, // delete
88  *     });
89  * ```
90  *
91  * And you can call its enumerate() method to retrieve a KeyValueEnumerator,
92  * which is described below.
93  */
94 class KeyValueDatabase {
95   constructor(database) {
96     this.database = database;
97   }
99   put(key, value) {
100     return promisify(this.database.put, key, value);
101   }
103   /**
104    * Writes multiple key/value pairs to the database.
105    *
106    * Note:
107    *   * Each write could be either put or delete.
108    *   * Given multiple values with the same key, only the last value will be stored.
109    *   * If the same key gets put and deleted for multiple times, the final state
110    *     of that key is subject to the ordering of the put(s) and delete(s).
111    *
112    * @param pairs Pairs could be any of following types:
113    *        * An Object, all its properties and the corresponding values will
114    *          be used as key value pairs. A property with null or undefined indicating
115    *          a deletion.
116    *        * An Array or an iterable whose elements are key-value pairs. such as
117    *          [["key1", "value1"], ["key2", "value2"]]. Use a pair with value null
118    *          to delete a key-value pair, e.g. ["delete-key", null].
119    *        * A Map. A key with null or undefined value indicating a deletion.
120    * @return A promise that is fulfilled when all the key/value pairs are written
121    *         to the database.
122    */
123   writeMany(pairs) {
124     if (!pairs) {
125       throw new Error("writeMany(): unexpected argument.");
126     }
128     let entries;
130     if (
131       pairs instanceof Map ||
132       pairs instanceof Array ||
133       typeof pairs[Symbol.iterator] === "function"
134     ) {
135       try {
136         // Let Map constructor validate the argument. Note that Map remembers
137         // the original insertion order of the keys, which satisfies the ordering
138         // premise of this function.
139         const map = pairs instanceof Map ? pairs : new Map(pairs);
140         entries = Array.from(map, ([key, value]) => ({ key, value }));
141       } catch (error) {
142         throw new Error("writeMany(): unexpected argument.");
143       }
144     } else if (typeof pairs === "object") {
145       entries = Array.from(Object.entries(pairs), ([key, value]) => ({
146         key,
147         value,
148       }));
149     } else {
150       throw new Error("writeMany(): unexpected argument.");
151     }
153     if (entries.length) {
154       return promisify(this.database.writeMany, entries);
155     }
156     return Promise.resolve();
157   }
159   has(key) {
160     return promisify(this.database.has, key);
161   }
163   get(key, defaultValue) {
164     return promisify(this.database.get, key, defaultValue);
165   }
167   delete(key) {
168     return promisify(this.database.delete, key);
169   }
171   clear() {
172     return promisify(this.database.clear);
173   }
175   async enumerate(from_key, to_key) {
176     return new KeyValueEnumerator(
177       await promisify(this.database.enumerate, from_key, to_key)
178     );
179   }
183  * A class that wraps an nsIKeyValueEnumerator in a Promise-based API.
185  * This class isn't exported, so you can't instantiate it directly, but you
186  * can retrieve an instance of this class by calling enumerate() on an instance
187  * of KeyValueDatabase:
189  * ```
190  *     const database = await KeyValueService.getOrCreate(path, name);
191  *     const enumerator = await database.enumerate();
192  * ```
194  * And then iterate pairs via its hasMoreElements() and getNext() methods:
196  * ```
197  *     while (enumerator.hasMoreElements()) {
198  *       const { key, value } = enumerator.getNext();
199  *       …
200  *     }
201  * ```
203  * Or with a `for...of` statement:
205  * ```
206  *     for (const { key, value } of enumerator) {
207  *         …
208  *     }
209  * ```
210  */
211 class KeyValueEnumerator {
212   constructor(enumerator) {
213     this.enumerator = enumerator;
214   }
216   hasMoreElements() {
217     return this.enumerator.hasMoreElements();
218   }
220   getNext() {
221     return this.enumerator.getNext();
222   }
224   *[Symbol.iterator]() {
225     while (this.enumerator.hasMoreElements()) {
226       yield this.enumerator.getNext();
227     }
228   }