1local datamanager = require "core.storagemanager".olddm;
2local array = require "util.array";
3local datetime = require "util.datetime";
4local st = require "util.stanza";
5local now = require "util.time".now;
6local id = require "util.id".medium;
7
8local host = module.host;
9
10local driver = {};
11
12function driver:open(store, typ)
13	local mt = self[typ or "keyval"]
14	if not mt then
15		return nil, "unsupported-store";
16	end
17	return setmetatable({ store = store, type = typ }, mt);
18end
19
20function driver:stores(username) -- luacheck: ignore 212/self
21	return datamanager.stores(username, host);
22end
23
24function driver:purge(user) -- luacheck: ignore 212/self
25	return datamanager.purge(user, host);
26end
27
28local keyval = { };
29driver.keyval = { __index = keyval };
30
31function keyval:get(user)
32	return datamanager.load(user, host, self.store);
33end
34
35function keyval:set(user, data)
36	return datamanager.store(user, host, self.store, data);
37end
38
39function keyval:users()
40	return datamanager.users(host, self.store, self.type);
41end
42
43local archive = {};
44driver.archive = { __index = archive };
45
46function archive:append(username, key, value, when, with)
47	when = when or now();
48	if not st.is_stanza(value) then
49		return nil, "unsupported-datatype";
50	end
51	value = st.preserialize(st.clone(value));
52	value.when = when;
53	value.with = with;
54	value.attr.stamp = datetime.datetime(when);
55	value.attr.stamp_legacy = datetime.legacy(when);
56
57	if key then
58		local items, err = datamanager.list_load(username, host, self.store);
59		if not items and err then return items, err; end
60		if items then
61			items = array(items);
62			items:filter(function (item)
63				return item.key ~= key;
64			end);
65			value.key = key;
66			items:push(value);
67			local ok, err = datamanager.list_store(username, host, self.store, items);
68			if not ok then return ok, err; end
69			return key;
70		end
71	else
72		key = id();
73	end
74
75	value.key = key;
76
77	local ok, err = datamanager.list_append(username, host, self.store, value);
78	if not ok then return ok, err; end
79	return key;
80end
81
82function archive:find(username, query)
83	local items, err = datamanager.list_load(username, host, self.store);
84	if not items then
85		if err then
86			return items, err;
87		else
88			return function () end, 0;
89		end
90	end
91	local count = #items;
92	local i = 0;
93	if query then
94		items = array(items);
95		if query.key then
96			items:filter(function (item)
97				return item.key == query.key;
98			end);
99		end
100		if query.with then
101			items:filter(function (item)
102				return item.with == query.with;
103			end);
104		end
105		if query.start then
106			items:filter(function (item)
107				local when = item.when or datetime.parse(item.attr.stamp);
108				return when >= query.start;
109			end);
110		end
111		if query["end"] then
112			items:filter(function (item)
113				local when = item.when or datetime.parse(item.attr.stamp);
114				return when <= query["end"];
115			end);
116		end
117		count = #items;
118		if query.reverse then
119			items:reverse();
120			if query.before then
121				for j = 1, count do
122					if (items[j].key or tostring(j)) == query.before then
123						i = j;
124						break;
125					end
126				end
127			end
128		elseif query.after then
129			for j = 1, count do
130				if (items[j].key or tostring(j)) == query.after then
131					i = j;
132					break;
133				end
134			end
135		end
136		if query.limit and #items - i > query.limit then
137			items[i+query.limit+1] = nil;
138		end
139	end
140	return function ()
141		i = i + 1;
142		local item = items[i];
143		if not item then return; end
144		local key = item.key or tostring(i);
145		local when = item.when or datetime.parse(item.attr.stamp);
146		local with = item.with;
147		item.key, item.when, item.with = nil, nil, nil;
148		item.attr.stamp = nil;
149		item.attr.stamp_legacy = nil;
150		item = st.deserialize(item);
151		return key, item, when, with;
152	end, count;
153end
154
155function archive:dates(username)
156	local items, err = datamanager.list_load(username, host, self.store);
157	if not items then return items, err; end
158	return array(items):pluck("when"):map(datetime.date):unique();
159end
160
161function archive:delete(username, query)
162	if not query or next(query) == nil then
163		return datamanager.list_store(username, host, self.store, nil);
164	end
165	local items, err = datamanager.list_load(username, host, self.store);
166	if not items then
167		if err then
168			return items, err;
169		end
170		-- Store is empty
171		return 0;
172	end
173	items = array(items);
174	local count_before = #items;
175	if query then
176		if query.key then
177			items:filter(function (item)
178				return item.key ~= query.key;
179			end);
180		end
181		if query.with then
182			items:filter(function (item)
183				return item.with ~= query.with;
184			end);
185		end
186		if query.start then
187			items:filter(function (item)
188				return item.when < query.start;
189			end);
190		end
191		if query["end"] then
192			items:filter(function (item)
193				return item.when > query["end"];
194			end);
195		end
196		if query.truncate and #items > query.truncate then
197			if query.reverse then
198				-- Before: { 1, 2, 3, 4, 5, }
199				-- After: { 1, 2, 3 }
200				for i = #items, query.truncate + 1, -1 do
201					items[i] = nil;
202				end
203			else
204				-- Before: { 1, 2, 3, 4, 5, }
205				-- After: { 3, 4, 5 }
206				local offset = #items - query.truncate;
207				for i = 1, #items do
208					items[i] = items[i+offset];
209				end
210			end
211		end
212	end
213	local count = count_before - #items;
214	if count == 0 then
215		return 0; -- No changes, skip write
216	end
217	local ok, err = datamanager.list_store(username, host, self.store, items);
218	if not ok then return ok, err; end
219	return count;
220end
221
222module:provides("storage", driver);
223