1 /** 2 * ae.sys.persistence.stringset 3 * 4 * License: 5 * This Source Code Form is subject to the terms of 6 * the Mozilla Public License, v. 2.0. If a copy of 7 * the MPL was not distributed with this file, You 8 * can obtain one at http://mozilla.org/MPL/2.0/. 9 * 10 * Authors: 11 * Vladimir Panteleev <ae@cy.md> 12 */ 13 14 module ae.sys.persistence.stringset; 15 16 import ae.sys.persistence.core; 17 18 // **************************************************************************** 19 20 // https://issues.dlang.org/show_bug.cgi?id=7016 21 static import ae.sys.file; 22 23 /// A string hashset, stored one line per entry. 24 struct PersistentStringSet 25 { 26 import ae.utils.aa : HashSet; 27 28 static HashSet!string _load(string fileName) 29 { 30 import std.file : readText; 31 import std.string : splitLines; 32 33 return HashSet!string(fileName.readText().splitLines()); 34 } /// 35 36 static void _save(string fileName, HashSet!string data) 37 { 38 import std.array : join; 39 import ae.sys.file : atomicWrite; 40 41 atomicWrite(fileName, data.keys.join("\n")); 42 } /// 43 44 private alias Cache = FileCache!(_load, _save, FlushPolicy.manual); 45 private Cache cache; 46 47 this(string fileName) { cache = Cache(fileName); } /// 48 49 auto opBinaryRight(string op)(string key) 50 if (op == "in") 51 { 52 return key in cache; 53 } /// 54 55 void add(string key) 56 { 57 assert(key !in cache); 58 cache.add(key); 59 cache.save(); 60 } /// 61 62 void remove(string key) 63 { 64 assert(key in cache); 65 cache.remove(key); 66 cache.save(); 67 } /// 68 69 @property string[] lines() { return cache.keys; } /// 70 @property size_t length() { return cache.length; } /// 71 } 72 73 /// 74 unittest 75 { 76 import std.file, std.conv, core.thread; 77 78 enum FN = "test.txt"; 79 if (FN.exists) remove(FN); 80 scope(exit) if (FN.exists) remove(FN); 81 82 { 83 auto s = PersistentStringSet(FN); 84 assert("foo" !in s); 85 assert(s.length == 0); 86 s.add("foo"); 87 } 88 { 89 auto s = PersistentStringSet(FN); 90 assert("foo" in s); 91 assert(s.length == 1); 92 s.remove("foo"); 93 } 94 { 95 auto s = PersistentStringSet(FN); 96 assert("foo" !in s); 97 Thread.sleep(filesystemTimestampGranularity); 98 std.file.write(FN, "foo\n"); 99 assert("foo" in s); 100 Thread.sleep(filesystemTimestampGranularity); 101 std.file.write(FN, "bar\n"); 102 assert(s.lines == ["bar"], text(s.lines)); 103 } 104 }