-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathapi_client.lua
137 lines (112 loc) · 5.26 KB
/
api_client.lua
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
-- for programatic interaction with the api
local shared = require("shared")
local ecnet2 = require("ecnet2")
--- @class memdbclient
--- @field list_cmd fun(): {data: string} get a list of available commands
--- @field help fun(command: "list_cmd"|"help"|"get"|"set"|"get_id"|"safe_set"|"append"|"prepend"|"incr"|"decr"|"del"): {data: string} get help with a specific function
--- @field get fun(path: string): {data: any}|{error: string} get a stored value
--- @field set fun(path: string, val: any, lifetime?: number): {data: boolean}|{error: string} set a stored value, creating it if it doesnt exist
--- @field get_id fun(path: string): {data: string}|{error: string} get a unique id for the value stored at that path, used with safe_set
--- @field safe_set fun(path: string, val: any, id: string, lifetime?: number): {data: boolean}|{error: string} set a stored value, pass an id from get_id to prevent overwriting the value if it was changed since you fetched the id
--- @field append fun(path: string, val: any, lifetime?: number): {data: boolean}|{error: string} appends to a stored list or string
--- @field prepend fun(path: string, val: any, lifetime?: number): {data: boolean}|{error: string} prepends to a stored list or string
--- @field incr fun(path: string, val: any, lifetime?: number): {data: boolean}|{error: string} increments a stored number
--- @field decr fun(path: string, val: any, lifetime?: number): {data: boolean}|{error: string} decrements a stored number
--- @field del fun(path: string): {data: boolean}|{error: string} delete a stored value
--- create a new memdb client
--- @param server string the address of the server to communicate with
--- @param client_id string an id for this client, this should be kept secret and not easily guessable -- clients with the same id share state
--- @param request_timeout number the amount of time in seconds to wait before timing out when receiving a server response
--- @return memdbclient client configured memdb client
local new_client = function(server, client_id, request_timeout)
shared.init_random()
ecnet2.open("top")
local client = {
_server = server,
_client_id = client_id,
_connection = nil,
_timeout = request_timeout,
_id = ecnet2.Identity("/.ecnet2"),
}
client._memdb = client._id:Protocol {
name = "memdb",
serialize = textutils.serialize,
deserialize = textutils.unserialize,
}
client._do_network_action = function(action)
local collected_result = nil
local time_start = os.epoch("utc")
local main = function()
client._connection = client._memdb:connect(server, "top")
client._connection:receive(client._timeout) -- yield to let the daemon process
client._connection:send("client_id=" .. client_id)
client._connection:receive(client._timeout) -- yield to let the daemon process and to clear the queue
collected_result = action()
end
while not collected_result and (os.epoch("utc") - time_start) / 1000 < client._timeout do
parallel.waitForAny(main, ecnet2.daemon)
end
if not collected_result then
error("request timed out")
end
return collected_result
end
local _build_command_str = function(cmd)
if cmd.id then -- special handling for safe_set
return (cmd.cmd or "") ..
" " ..
(cmd.path or "") ..
(cmd.val and (" " .. (cmd.val .. " " .. cmd.id .. " " .. (cmd.lifetime or ""))))
end
return (cmd.cmd or "") ..
" " ..
(cmd.path or "") ..
(cmd.val and ((" " .. cmd.val .. (cmd.lifetime and " " .. cmd.lifetime or ""))) or "")
end
client._send_memdb_cmd = function(cmd, path, val, lifetime, id)
return client._do_network_action(function()
if not client._connection then
error("client not connected")
end
client._connection:send(_build_command_str({
cmd = cmd,
path = path,
val = val,
lifetime = lifetime,
id = id
}))
return textutils.unserialize(select(2, client._connection:receive(client._timeout)))
end)
end
client.list_cmd = function()
return client._send_memdb_cmd("list_cmd")
end
client.help = function(command)
return client._send_memdb_cmd("help", command)
end
client.get = function(path)
return client._send_memdb_cmd("get", path)
end
local _setter = function(cmd)
return function(path, val, lifetime, id)
return client._send_memdb_cmd(cmd, path, textutils.serialize(val, { compact = (type(val) == "table") }),
lifetime, id)
end
end
client.set = _setter("set")
client.get_id = function(path)
return client._send_memdb_cmd("get_id", path)
end
client.safe_set = _setter("safe_set")
client.append = _setter("append")
client.prepend = _setter("prepend")
client.incr = _setter("incr")
client.decr = _setter("decr")
client.del = function(path)
return client._send_memdb_cmd("del", path)
end
return client
end
return {
new_client = new_client
}