Overview
| Comment: | iterating |
|---|---|
| Downloads: | Tarball | ZIP archive | SQL archive |
| Timelines: | family | ancestors | descendants | both | trunk |
| Files: | files | file ages | folders |
| SHA3-256: |
59e1d7d56aea6174c52dbedafd74e94b |
| User & Date: | lexi on 2020-12-16 08:46:02 |
| Other Links: | manifest | tags |
Context
|
2020-12-16
| ||
| 08:46 | add nix build file check-in: df4ae251ef user: lexi tags: trunk | |
| 08:46 | iterating check-in: 59e1d7d56a user: lexi tags: trunk | |
|
2020-12-14
| ||
| 14:40 | more boilerplate, add template framework check-in: 6f17de4767 user: lexi tags: trunk | |
Changes
Added backend/pgsql.t version [0360541ecf].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 |
-- vim: ft=terra local queries = { conf_get = { params = {rawstring}, sql = [[ select value from parsav_config where key = $1::text limit 1 ]]; }; conf_set = { params = {rawstring,rawstring}, sql = [[ insert into parsav_config (key, value) values ($1::text, $2::text) on conflict (key) do update set value = $2::text ]]; }; conf_reset = { params = {rawstring}, sql = [[ delete from parsav_config where key = $1::text ]]; }; actor_fetch_uid = { params = {uint64}, sql = [[ select id, nym, handle, origin, bio, rank, quota, key from parsav_actors where id = $1::bigint ]]; }; actor_fetch_xid = { params = {rawstring}, sql = [[ select a.id, a.nym, a.handle, a.origin, a.bio, a.rank, a.quota, a.key, coalesce(s.domain, (select value from parsav_config where key='domain' limit 1)) as domain from parsav_actors as a left join parsav_servers as s on a.origin = s.id where $1::text = (a.handle || '@' || domain) or $1::text = ('@' || a.handle || '@' || domain) or (a.origin is null and $1::text = ('@' || a.handle)) ]]; }; } local struct pqr { sz: intptr res: &lib.pq.PGresult } terra pqr:free() if self.sz > 0 then lib.pq.PQclear(self.res) end end terra pqr:null(row: intptr, col: intptr) return (lib.pq.PQgetisnull(self.res, row, col) == 1) end terra pqr:string(row: intptr, col: intptr) var v = lib.pq.PQgetvalue(self.res, row, col) var r: lib.mem.ptr(int8) r.ct = lib.str.sz(v) r.ptr = lib.str.ndup(v, r.ct) return r end pqr.methods.int = macro(function(self, ty, row, col) return quote var i: ty:astype() var v = lib.pq.PQgetvalue(self.res, row, col) lib.math.netswap_ip(ty, v, &i) in i end end) local con = symbol(&lib.pq.PGconn) local prep = {} for k,q in pairs(queries) do local qt = (q.sql):gsub('%s+',' '):gsub('^%s*(.-)%s*$','%1') local stmt = 'parsavpg_' .. k prep[#prep + 1] = quote var res = lib.pq.PQprepare([con], stmt, qt, [#q.params], nil) defer lib.pq.PQclear(res) if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_COMMAND_OK then if res == nil then lib.bail('grievous error occurred preparing ',k,' statement') end lib.bail('could not prepare PGSQL statement ',k,': ',lib.pq.PQresultErrorMessage(res)) end lib.dbg('prepared PGSQL statement ',k) end local args, casts, counters, fixers, ft, yield = {}, {}, {}, {}, {}, {} for i, ty in ipairs(q.params) do args[i] = symbol(ty) ft[i] = `1 if ty == rawstring then counters[i] = `lib.trn([args[i]] == nil, 0, lib.str.sz([args[i]])) casts[i] = `[&int8]([args[i]]) elseif ty:isintegral() then counters[i] = ty.bytes casts[i] = `[&int8](&[args[i]]) fixers[#fixers + 1] = quote --lib.io.fmt('uid=%llu(%llx)\n',[args[i]],[args[i]]) [args[i]] = lib.math.netswap(ty, [args[i]]) end end end q.exec = terra(src: &lib.store.source, [args]) var params = arrayof([&int8], [casts]) var params_sz = arrayof(int, [counters]) var params_ft = arrayof(int, [ft]) [fixers] var res = lib.pq.PQexecPrepared([&lib.pq.PGconn](src.handle), stmt, [#args], params, params_sz, params_ft, 1) if res == nil then lib.bail(['grievous error occurred executing '..k..' against database']) elseif lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then lib.bail(['PGSQL database procedure '..k..' failed\n'], lib.pq.PQresultErrorMessage(res)) end var ct = lib.pq.PQntuples(res) if ct == 0 then lib.pq.PQclear(res) return pqr {0, nil} else return pqr {ct, res} end end end local terra row_to_actor(r: &pqr, row: intptr): lib.store.actor var a = lib.store.actor { id = r:int(uint64, row, 0); nym = r:string(row, 1); handle = r:string(row, 2); bio = r:string(row, 4); key = r:string(row, 7); rights = lib.store.rights_default(); } a.rights.rank = r:int(uint16, 0, 5); a.rights.quota = r:int(uint32, 0, 6); if r:null(0,3) then a.origin = 0 else a.origin = r:int(uint64,0,3) end return a end local b = `lib.store.backend { id = "pgsql"; open = [terra(src: &lib.store.source): &opaque lib.report('connecting to postgres database: ', src.string.ptr) var [con] = lib.pq.PQconnectdb(src.string.ptr) if lib.pq.PQstatus(con) ~= lib.pq.CONNECTION_OK then lib.warn('postgres backend connection failed') lib.pq.PQfinish(con) return nil end var res = lib.pq.PQexec(con, [[ select pg_catalog.set_config('search_path', 'public', false) ]]) if res ~= nil then defer lib.pq.PQclear(res) end if res == nil or lib.pq.PQresultStatus(res) ~= lib.pq.PGRES_TUPLES_OK then lib.warn('failed to secure postgres connection') lib.pq.PQfinish(con) return nil end [prep] return con end]; close = [terra(src: &lib.store.source) lib.pq.PQfinish([&lib.pq.PGconn](src.handle)) end]; conf_get = [terra(src: &lib.store.source, key: rawstring) var r = queries.conf_get.exec(src, key) if r.sz == 0 then return [lib.mem.ptr(int8)] { ptr = nil, ct = 0 } else defer r:free() return r:string(0,0) end end]; conf_set = [terra(src: &lib.store.source, key: rawstring, val: rawstring) queries.conf_set.exec(src, key, val):free() end]; conf_reset = [terra(src: &lib.store.source, key: rawstring) queries.conf_reset.exec(src, key):free() end]; actor_fetch_uid = [terra(src: &lib.store.source, uid: uint64) var r = queries.actor_fetch_uid.exec(src, uid) if r.sz == 0 then return [lib.stat(lib.store.actor)] { ok = false, error = 1} else defer r:free() var a = [lib.stat(lib.store.actor)] { ok = true } a.val = row_to_actor(&r, 0) a.val.source = src return a end end]; } return b |
Modified cmdparse.t from [8abc7a6fc7] to [c7f162fae3].
1 2 3 4 5 6 7 |
return function(tbl)
local options = terralib.types.newstruct('options') do
local flags = '' for _,d in pairs(tbl) do flags = flags .. d[1] end
local helpstr = 'usage: parsav [-' .. flags .. '] [<arg>...]\n'
options.entries = {
{field = 'arglist', type = lib.mem.ptr(rawstring)}
}
|
> |
1 2 3 4 5 6 7 8 |
-- vim: ft=terra
return function(tbl)
local options = terralib.types.newstruct('options') do
local flags = '' for _,d in pairs(tbl) do flags = flags .. d[1] end
local helpstr = 'usage: parsav [-' .. flags .. '] [<arg>...]\n'
options.entries = {
{field = 'arglist', type = lib.mem.ptr(rawstring)}
}
|
Modified config.lua from [85408f7c8a] to [60797f7bc4].
19 20 21 22 23 24 25 26 27 28 29 30 31 32 |
dist = default('parsav_dist', coalesce(
os.getenv('NIX_PATH') and 'nixos',
os.getenv('NIX_STORE') and 'nixos',
''));
tgttrip = default('parsav_arch_triple'); -- target triple, used in xcomp
tgtcpu = default('parsav_arch_cpu'); -- target cpu, used in xcomp
tgthf = u.tobool(default('parsav_arch_armhf',true));
build = {
id = u.rndstr(6);
release = u.ingest('release');
when = os.date();
};
feat = {};
}
|
> |
19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 |
dist = default('parsav_dist', coalesce(
os.getenv('NIX_PATH') and 'nixos',
os.getenv('NIX_STORE') and 'nixos',
''));
tgttrip = default('parsav_arch_triple'); -- target triple, used in xcomp
tgtcpu = default('parsav_arch_cpu'); -- target cpu, used in xcomp
tgthf = u.tobool(default('parsav_arch_armhf',true));
endian = default('parsav_arch_endian', 'little');
build = {
id = u.rndstr(6);
release = u.ingest('release');
when = os.date();
};
feat = {};
}
|
Added file.t version [fc7770c3f7].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra -- TODO: add support for windows IO calls local handle_type = int local posix = terralib.includec 'fcntl.h' local unistd = terralib.includec 'unistd.h' struct file { handle: handle_type read: bool write: bool } file.mode = { read = 0, write = 1, rw = 2 } file.seek = { abs = 0, ofs = 1, eof = 2 } file.methods = { open = terra(path: rawstring, mode: uint8) var f: file var flag: int if mode == [file.mode.rw] then flag = posix.O_RDWR f.read = true f.write = true elseif mode == [file.mode.read] then flag = posix.O_RDONLY f.read = true f.write = false elseif mode == [file.mode.read] then flag = posix.O_WRONLY f.read = false f.write = true else lib.bail('invalid file mode') end lib.dbg('opening file ', path) f.handle = posix.open(path, flag) var r: lib.stat(file) if f.handle == -1 then r.ok = false r.error = 1 -- TODO get errno somehow? else r.ok = true r.val = f end return r end; close = terra(self: &file) unistd.close(self.handle) self.handle = -1 self.read = false self.write = false end; read = terra(self: &file, dest: rawstring, sz: intptr): ptrdiff return unistd.read(self.handle,dest,sz) end; write = terra(self: &file, data: &opaque, sz: intptr): ptrdiff return unistd.write(self.handle,data,sz) end; seek = terra(self: &file, ofs: ptrdiff, wh: int) var whence: int if wh == [file.seek.abs] then whence = unistd.SEEK_SET elseif wh == [file.seek.ofs] then whence = unistd.SEEK_CUR elseif wh == [file.seek.eof] then whence = unistd.SEEK_END else lib.bail('invalid seek mode') end return unistd.lseek(self.handle, ofs, whence) end; } return file |
Modified makefile from [0f98e9b572] to [40c8adaeb1].
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
lib/mbedtls/library/libmbedcrypto.a \ lib/mbedtls/library/libmbedx509.a dep.mongoose: lib/mongoose/libmongoose.a dep.json-c: lib/json-c/libjson-c.a lib: mkdir $@ # parsav is designed to be fronted by a real web # server like nginx if SSL is to be used # generate a shim static library so mongoose cooperates # with the build apparatus lib/mongoose/libmongoose.a: lib/mongoose lib/mongoose/mongoose.c lib/mongoose/mongoose.h $(CC) -c $</mongoose.c -o lib/mongoose/mongoose.o \ -DMG_ENABLE_THREADS \ -DMG_ENABLE_IPV6 \ -DMG_ENABLE_HTTP_WEBDAV \ -DMG_ENABLE_HTTP_WEBSOCKET=0 ar rcs $@ lib/mongoose/*.o ................................................................................ lib/json-c/libjson-c.a: lib/json-c/Makefile $(MAKE) -C lib/json-c lib/mbedtls/library/%.a: lib/mbedtls $(MAKE) -C lib/mbedtls/library $*.a ifeq ($(dl), git) lib/mongoose: lib cd lib && git clone https://github.com/cesanta/mongoose lib/mbedtls: lib cd lib && git clone https://github.com/ARMmbed/mbedtls.git lib/json-c: lib cd lib && git clone https://github.com/json-c/json-c.git else lib/%: lib/%.tar.gz cd lib && tar zxf $*.tar.gz |
|
|
|
|
|
|
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
..
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
|
lib/mbedtls/library/libmbedcrypto.a \ lib/mbedtls/library/libmbedx509.a dep.mongoose: lib/mongoose/libmongoose.a dep.json-c: lib/json-c/libjson-c.a lib: mkdir $@ # generate a shim static library so mongoose cooperates # with the build apparatus. note that parsav is designed # to be fronted by a real web server like nginx if SSL # is to be used, so we don't turn on SSL in mongoose lib/mongoose/libmongoose.a: lib/mongoose lib/mongoose/mongoose.c lib/mongoose/mongoose.h $(CC) -c $</mongoose.c -o lib/mongoose/mongoose.o \ -DMG_ENABLE_THREADS \ -DMG_ENABLE_IPV6 \ -DMG_ENABLE_HTTP_WEBDAV \ -DMG_ENABLE_HTTP_WEBSOCKET=0 ar rcs $@ lib/mongoose/*.o ................................................................................ lib/json-c/libjson-c.a: lib/json-c/Makefile $(MAKE) -C lib/json-c lib/mbedtls/library/%.a: lib/mbedtls $(MAKE) -C lib/mbedtls/library $*.a ifeq ($(dl), git) lib/mongoose: lib cd lib && git clone https://github.com/cesanta/mongoose.git lib/mbedtls: lib cd lib && git clone https://github.com/ARMmbed/mbedtls.git lib/json-c: lib cd lib && git clone https://github.com/json-c/json-c.git else lib/%: lib/%.tar.gz cd lib && tar zxf $*.tar.gz |
Added math.t version [fe958b6645].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
-- vim: ft=terra local m = { shorthand = {maxlen = 14} } -- swap in place -- faster on little endian m.netswap_ip = macro(function(ty, src, dest) if ty:astype().type ~= 'integer' then error('bad type') end local bytes = ty:astype().bytes src = `[&uint8](src) dest = `[&uint8](dest) if config.endian == 'little' then return quote for i = 0, bytes do dest[i] = src[bytes - (i+1)] end end elseif config.endian == 'big' then return quote for i = 0, bytes do dest[i] = src[i] end end else error('unknown endianness '..config.endian) end end) -- swap out of place -- safer, more flexible, and optimized to an intrinsic call; trivial on big endian m.netswap = macro(function(tyq, src) if config.endian == 'little' then local ty = tyq:astype() local a,b = symbol(ty), symbol(ty) local bytes = ty.bytes local steps = {} for i=0,bytes-1 do steps[#steps + 1] = quote b = b << 8 b = b or (a and 0xff) a = a >> 8 end end return quote var [a] = src var [b] = 0 [steps] in b end elseif config.endian == 'big' then return `src else error('unknown endianness '..config.endian) end end) terra m.shorthand.cval(character: int8): {uint8, bool} var ch = [uint8](character) if ch >= 0x30 and ch <= 0x39 then ch = 00 + (ch - 0x30) elseif ch == 0x2d then ch = 10 elseif ch >= 0x41 and ch <= 0x5a then ch = 11 + (ch - 0x41) elseif ch == 0x3a then ch = 37 elseif ch >= 0x61 and ch <= 0x7a then ch = 38 + (ch - 0x61) else return 0, false end return ch, true end terra m.shorthand.gen(val: uint64, dest: rawstring): ptrdiff var lst = "0123456789-ABCDEFGHIJKLMNOPQRSTUVWXYZ:abcdefghijklmnopqrstuvwxyz" var buf: int8[m.shorthand.maxlen] var ptr = [&int8](buf) while val ~= 0 do var v = val % 64 @ptr = lst[v] ptr = ptr + 1 val = val / 64 end var len = ptr - buf for i = 0, len do dest[i] = buf[len - (i+1)] end dest[len] = 0 return len end terra m.shorthand.parse(s: rawstring, len: intptr): {uint64, bool} var val: uint64 = 0 for i = 0, len do var v, ok = m.shorthand.cval(s[i]) if ok == false then return 0, false end val = (val * 64) + v end return val, true end return m |
Modified parsav.t from [889180c92d] to [ce122c09dc].
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 .. 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 ... 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 ... 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 ... 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 |
code[#code+1] = quote var n = v in
lib.io.send(2, n, lib.str.sz(n)) end
end
end
code[#code+1] = `lib.io.send(2, '\n', 1)
return code
end;
proc = {
exit = terralib.externfunction('exit', int -> {});
getenv = terralib.externfunction('getenv', rawstring -> rawstring);
};
io = {
open = terralib.externfunction('open', {rawstring, int} -> int);
close = terralib.externfunction('close', {int} -> int);
send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff);
recv = terralib.externfunction('read', {int, rawstring, intptr} -> ptrdiff);
say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end);
fmt = terralib.externfunction('printf',
terralib.types.funcpointer({rawstring},{int},true));
};
str = {
sz = terralib.externfunction('strlen', rawstring -> intptr);
cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int);
cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring);
ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring);
fmt = terralib.externfunction('asprintf',
terralib.types.funcpointer({&rawstring},{int},true));
};
mem = {
zero = macro(function(r)
return quote
for i = 0, [r.tree.type.N] do r[i] = 0 end
end
end);
heapa_raw = terralib.externfunction('malloc', intptr -> &opaque);
................................................................................
ct = sz;
}
end)
};
}
local noise = global(uint8,1)
local defrep = function(level,n,code)
return macro(function(...)
local q = lib.emit("\27["..code..";1m(parsav "..n..")\27[m ", ...)
return quote
if noise >= level then [q] end
end
end);
end
lib.dbg = defrep(3,'debug', '32')
lib.report = defrep(2,'info', '35')
lib.warn = defrep(1,'warn', '33')
lib.bail = macro(function(...)
local q = lib.emit("\27[31;1m(parsav fatal)\27[m ", ...)
return quote
[q]
lib.proc.exit(1)
end
end);
lib.mem.ptr = terralib.memoize(function(ty)
local t = terralib.types.newstruct(string.format('ptr<%s>', ty))
t.entries = {
{'ptr', &ty};
{'ct', intptr};
}
local recurse = false
if ty:isstruct() then
if ty.methods.free then recurse = true end
end
t.methods = {
free = terra(self: &t): bool
[recurse and quote
................................................................................
self.ct = newct
return true
else return false end
end;
}
return t
end)
lib.err = lib.loadlib('mbedtls','mbedtls/error.h')
lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h')
lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h')
lib.md = lib.loadlib('mbedtls','mbedtls/md.h')
lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h')
lib.net = lib.loadlib('mongoose','mongoose.h')
lib.pq = lib.loadlib('libpq','libpq-fe.h')
lib.crypt = terralib.loadfile('crypt.t')()
lib.http = terralib.loadfile('http.t')()
lib.tpl = terralib.loadfile('tpl.t')()
lib.string = terralib.loadfile('string.t')()
lib.store = terralib.loadfile('store.t')()
lib.cmdparse = terralib.loadfile('cmdparse.t')()
do local collate = function(path,f, ...)
return loadfile(path..'/'..f..'.lua')(path, ...)
end
data = {
view = collate('view','load');
} end
................................................................................
lib.crypt.pem(pub, &kp, buf)
lib.io.send(1, msg, [#msg])
lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf)))
lib.io.send(1, '\n', 1)
end
end)
local handle = {
http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque)
switch event do
case lib.net.MG_EV_HTTP_MSG then
lib.dbg('routing HTTP request')
var msg = [&lib.net.mg_http_message](p)
end
end
end;
}
do
local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when)
terra version() lib.io.send(1, p, [#p]) end
end
terra noise_init()
var n = lib.proc.getenv('parsav_noise')
if n ~= nil then
................................................................................
help = {'h', 'display this list'}
}
terra entry(argc: int, argv: &rawstring): int
noise_init()
[lib.init]
var mode: options
mode:parse(argc,argv)
if mode.version then
version()
return 0
end
if mode.help then
lib.io.send(1, [options.helptxt], [#options.helptxt])
return 0
end
var bind = lib.proc.getenv('parsav_bind')
if bind == nil then bind = '[::]:10917' end
var nm: lib.net.mg_mgr
lib.net.mg_mgr_init(&nm)
var nmc = lib.net.mg_http_listen(&nm, bind, handle.http, nil)
while true do
lib.net.mg_mgr_poll(&nm,1000)
end
lib.net.mg_mgr_free(&nm)
return 0
end
local bflag = function(long,short)
if short and util.has(buildopts, short) then return true end
if long and util.has(buildopts, long) then return true end
return false
|
> > > > > > > < < > > > > > > > > > > > > > > > < > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > < < < < < < < < < < < > > > < > | | < < < < < < < > > < |
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 .. 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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 ... 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 ... 285 286 287 288 289 290 291 292 293 294 295 296 297 298 ... 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 |
code[#code+1] = quote var n = v in lib.io.send(2, n, lib.str.sz(n)) end end end code[#code+1] = `lib.io.send(2, '\n', 1) return code end; trn = macro(function(cond, i, e) return quote var c: bool = [cond] var r: i.tree.type if c == true then r = i else r = e end in r end end); proc = { exit = terralib.externfunction('exit', int -> {}); getenv = terralib.externfunction('getenv', rawstring -> rawstring); }; io = { send = terralib.externfunction('write', {int, rawstring, intptr} -> ptrdiff); recv = terralib.externfunction('read', {int, rawstring, intptr} -> ptrdiff); say = macro(function(msg) return `lib.io.send(2, msg, [#(msg:asvalue())]) end); fmt = terralib.externfunction('printf', terralib.types.funcpointer({rawstring},{int},true)); }; str = { sz = terralib.externfunction('strlen', rawstring -> intptr); cmp = terralib.externfunction('strcmp', {rawstring, rawstring} -> int); ncmp = terralib.externfunction('strncmp', {rawstring, rawstring, intptr} -> int); cpy = terralib.externfunction('stpcpy',{rawstring, rawstring} -> rawstring); ncpy = terralib.externfunction('stpncpy',{rawstring, rawstring, intptr} -> rawstring); ndup = terralib.externfunction('strndup',{rawstring, intptr} -> rawstring); fmt = terralib.externfunction('asprintf', terralib.types.funcpointer({&rawstring},{int},true)); }; copy = function(tbl) local new = {} for k,v in pairs(tbl) do new[k] = v end setmetatable(new, getmetatable(tbl)) return new end; mem = { zero = macro(function(r) return quote for i = 0, [r.tree.type.N] do r[i] = 0 end end end); heapa_raw = terralib.externfunction('malloc', intptr -> &opaque); ................................................................................ ct = sz; } end) }; } local noise = global(uint8,1) local noise_header = function(code,txt,mod) if mod then return string.format('\27[%s;1m(parsav::%s %s)\27[m ', code,mod,txt) else return string.format('\27[%s;1m(parsav %s)\27[m ', code,txt) end end local defrep = function(level,n,code) return macro(function(...) local q = lib.emit(noise_header(code,n), ...) return quote if noise >= level then [q] end end end); end lib.dbg = defrep(3,'debug', '32') lib.report = defrep(2,'info', '35') lib.warn = defrep(1,'warn', '33') lib.bail = macro(function(...) local q = lib.emit(noise_header('31','fatal'), ...) return quote [q] lib.proc.exit(1) end end); lib.stat = terralib.memoize(function(ty) local n = struct { ok: bool union { error: uint8 val: ty } } n.name = string.format("stat<%s>", ty.name) n.stat_basetype = ty return n end) lib.enum = function(tbl) local ty = uint8 if #tbl >= 2^32 then ty = uint64 -- hey, can't be too safe elseif #tbl >= 2^16 then ty = uint32 elseif #tbl >= 2^8 then ty = uint16 end local o = { t = ty } for i, name in ipairs(tbl) do o[name] = i end return o end lib.mem.ptr = terralib.memoize(function(ty) local t = terralib.types.newstruct(string.format('ptr<%s>', ty)) t.entries = { {'ptr', &ty}; {'ct', intptr}; } t.ptr_basetype = ty local recurse = false if ty:isstruct() then if ty.methods.free then recurse = true end end t.methods = { free = terra(self: &t): bool [recurse and quote ................................................................................ self.ct = newct return true else return false end end; } return t end) lib.mem.vec = terralib.memoize(function(ty) local v = terralib.types.newstruct(string.format('vec<%s>', ty.name)) v.entries = { {field = 'storage', type = lib.mem.ptr(ty)}; {field = 'sz', type = intptr}; {field = 'run', type = intptr}; } local terra biggest(a: intptr, b: intptr) if a > b then return a else return b end end terra v:assure(n: intptr) if self.storage.ct < n then self.storage:resize(biggest(n, self.storage.ct + self.run)) end end v.methods = { init = terra(self: &v, run: intptr): bool if not self.storage:init(run) then return false end self.run = run self.sz = 0 return true end; new = terra(self: &v): &ty self:assure(self.sz + 1) self.sz = self.sz + 1 return self.storage.ptr + (self.sz - 1) end; push = terra(self: &v, val: ty) self:assure(self.sz + 1) self.storage.ptr[self.sz] = val self.sz = self.sz + 1 end; free = terra(self: &v) self.storage:free() end; last = terra(self: &v, idx: intptr): &ty if self.sz > idx then return self.storage.ptr + (self.sz - (idx+1)) else lib.bail('vector underrun!') end end; crush = terra(self: &v) self.storage:resize(self.sz) return self.storage end; } v.metamethods.__apply = terra(self: &v, idx: intptr): &ty -- no index?? if self.sz > idx then return self.storage.ptr + idx else lib.bail('vector overrun!') end end return v end) lib.err = lib.loadlib('mbedtls','mbedtls/error.h') lib.rsa = lib.loadlib('mbedtls','mbedtls/rsa.h') lib.pk = lib.loadlib('mbedtls','mbedtls/pk.h') lib.md = lib.loadlib('mbedtls','mbedtls/md.h') lib.b64 = lib.loadlib('mbedtls','mbedtls/base64.h') lib.net = lib.loadlib('mongoose','mongoose.h') lib.pq = lib.loadlib('libpq','libpq-fe.h') lib.file = terralib.loadfile('file.t')() lib.math = terralib.loadfile('math.t')() lib.crypt = terralib.loadfile('crypt.t')() lib.http = terralib.loadfile('http.t')() lib.tpl = terralib.loadfile('tpl.t')() lib.string = terralib.loadfile('string.t')() lib.store = terralib.loadfile('store.t')() local be = {} for _, b in pairs { 'pgsql' } do be[#be+1] = terralib.loadfile('backend/' .. b .. '.t')() end lib.store.backends = global(`array([be])) lib.cmdparse = terralib.loadfile('cmdparse.t')() lib.srv = terralib.loadfile('srv.t')() do local collate = function(path,f, ...) return loadfile(path..'/'..f..'.lua')(path, ...) end data = { view = collate('view','load'); } end ................................................................................ lib.crypt.pem(pub, &kp, buf) lib.io.send(1, msg, [#msg]) lib.io.send(1, [rawstring](&buf), lib.str.sz([rawstring](&buf))) lib.io.send(1, '\n', 1) end end) do local p = string.format('parsav: %s\nbuilt on %s\n', config.build.str, config.build.when) terra version() lib.io.send(1, p, [#p]) end end terra noise_init() var n = lib.proc.getenv('parsav_noise') if n ~= nil then ................................................................................ help = {'h', 'display this list'} } terra entry(argc: int, argv: &rawstring): int noise_init() [lib.init] -- shut mongoose the fuck up lib.net.mg_log_set_callback([terra(msg: &opaque, sz: int, u: &opaque) end], nil) var mode: options mode:parse(argc,argv) if mode.version then version() return 0 end if mode.help then lib.io.send(1, [options.helptxt], [#options.helptxt]) return 0 end var srv: lib.srv srv:start('backend.conf') lib.report('listening for requests') while true do srv:poll() end srv:shutdown() return 0 end local bflag = function(long,short) if short and util.has(buildopts, short) then return true end if long and util.has(buildopts, long) then return true end return false |
Added schema.sql version [2da83887bf].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 |
\prompt 'domain name: ' domain \prompt 'bind to socket: ' bind \qecho 'by default, parsav tracks rights on its own. you can override this later by replacing the rights table with a view, but you''ll then need to set appropriate rules on the view to allow administrators to modify rights from the web UI, or set the rights-readonly flag in the config table to true. for now, enter the name of an actor who will be granted full rights when she logs in.' \prompt 'admin actor: ' admin \qecho 'you will need to create an authentication view mapping your user database to something parsav can understand; see auth.sql for an example. enter the name of the view to use.' \prompt 'auth view: ' auth begin; drop table if exists parsav_config; create table if not exists parsav_config ( key text primary key, value text ); insert into parsav_config (key,value) values ('bind',:'bind'), ('domain',:'domain'), ('auth-source',:'auth'), ('administrator',:'admin'); -- note that valid ids should always > 0, as 0 is reserved for null -- on the client side, vastly simplifying code drop table if exists parsav_servers cascade; create table parsav_servers ( id bigint primary key default (1+random()*(2^63-1))::bigint, domain text not null, key bytea ); drop table if exists parsav_actors cascade; create table parsav_actors ( id bigint primary key default (1+random()*(2^63-1))::bigint, nym text, handle text not null, -- nym [@handle@origin] origin bigint references parsav_servers(id) on delete cascade, -- null origin = local actor bio text, rank smallint not null default 0, quota integer not null default 1000, key bytea, -- private if localactor; public if remote unique (handle,origin) ); drop table if exists parsav_rights cascade; create table parsav_rights ( key text, actor bigint references parsav_actors(id) on delete cascade, allow boolean, primary key (key,actor) ); insert into parsav_actors (handle,rank,quota) values (:'admin',1,0); insert into parsav_rights (actor,key,allow) select (select id from parsav_actors where handle=:'admin'), a.column1, a.column2 from (values ('ban',true), ('config',true), ('censor',true), ('suspend',true), ('rebrand',true) ) as a; drop table if exists parsav_posts cascade; create table parsav_posts ( id bigint primary key default (1+random()*(2^63-1))::bigint, author bigint references parsav_actors(id) on delete cascade, subject text, body text, posted timestamp not null, discovered timestamp not null, scope smallint not null, convo bigint, parent bigint, circles bigint[], mentions bigint[] ); drop table if exists parsav_conversations cascade; create table parsav_conversations ( id bigint primary key default (1+random()*(2^63-1))::bigint, uri text not null, discovered timestamp not null, head bigint references parsav_posts(id) ); drop table if exists parsav_rels cascade; create table parsav_rels ( relator bigint references parsav_actors(id) on delete cascade, -- e.g. follower relatee bigint references parsav_actors(id) on delete cascade, -- e.g. follower kind smallint, -- e.g. follow, block, mute primary key (relator, relatee, kind) ); drop table if exists parsav_acts cascade; create table parsav_acts ( id bigint primary key default (1+random()*(2^63-1))::bigint, kind text not null, -- like, react, so on time timestamp not null, actor bigint references parsav_actors(id) on delete cascade, subject bigint -- may be post or act, depending on kind ); drop table if exists parsav_log cascade; create table parsav_log ( -- accesses are tracked for security & sending delete acts id bigint primary key default (1+random()*(2^63-1))::bigint, time timestamp not null, actor bigint references parsav_actors(id) on delete cascade, post bigint not null ); end; |
Added srv.t version [350801ad24].
> > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 |
-- vim: ft=terra local util = dofile 'common.lua' local struct srv { sources: lib.mem.ptr(lib.store.source) webmgr: lib.net.mg_mgr webcon: &lib.net.mg_connection } local handle = { http = terra(con: &lib.net.mg_connection, event: int, p: &opaque, ext: &opaque) switch event do case lib.net.MG_EV_HTTP_MSG then lib.dbg('routing HTTP request') var msg = [&lib.net.mg_http_message](p) end end end; } local char = macro(function(ch) return `[string.byte(ch:asvalue())] end) local terra cfg(s: &srv, befile: rawstring) lib.report('configuring backends from ', befile) var fr = lib.file.open(befile, [lib.file.mode.read]) if fr.ok == false then lib.bail('could not open configuration file ', befile) end var f = fr.val var c: lib.mem.vec(lib.store.source) c:init(8) var text: lib.string.acc text:init(64) do var buf: int8[64] while true do var ct = f:read(buf, [buf.type.N]) if ct == 0 then break end text:push(buf, ct) end end f:close() var cur = text.buf var segs: tuple(&int8, &int8)[3] = array( {[&int8](0),[&int8](0)}, {[&int8](0),[&int8](0)}, {[&int8](0),[&int8](0)} ) var segdup = [terra(s: {rawstring, rawstring}) var sz = s._1 - s._0 var str = s._0 return [lib.mem.ptr(int8)] { ptr = lib.str.ndup(str, sz); ct = sz; } end] var fld = 0 while (cur - text.buf) < text.sz do if segs[fld]._0 == nil then if not (@cur == char(' ') or @cur == char('\t') or @cur == char('\n')) then segs[fld] = {cur, nil} end else if fld < 2 and @cur == char(' ') or @cur == char('\t') then segs[fld]._1 = cur fld = fld + 1 segs[fld] = {nil, nil} elseif @cur == char('\n') or cur == text.buf + (text.sz-1) then if fld < 2 then lib.bail('incomplete backend line in ', befile) else segs[fld]._1 = cur var src = c:new() src.id = segdup(segs[0]) src.string = segdup(segs[2]) src.backend = nil for i = 0,[lib.store.backends.type.N] do if lib.str.ncmp(segs[1]._0, lib.store.backends[i].id, segs[1]._1 - segs[1]._0) == 0 then src.backend = &lib.store.backends[i] break end end if src.backend == nil then lib.bail('unknown backend in ', befile) end src.handle = nil fld = 0 segs[0] = {nil, nil} end end end cur = cur + 1 end text:free() s.sources = c:crush() end --srv.methods.conf_set = terra(self: &srv, key: rawstring, val:rawstring) -- self.sources.ptr[0]:conf_set(key, val) --end srv.metamethods.__methodmissing = macro(function(meth, self, ...) local primary, ptr, stat, simple = 0,1,2,3 local tk, rt = primary local expr = {...} for _,f in pairs(lib.store.backend.entries) do local fn = f.field or f[1] local ft = f.type or f[2] if fn == meth then rt = ft.type.returntype if rt == bool then tk = simple elseif rt.stat_basetype then tk = stat elseif rt.ptr_basetype then tk = ptr end break end end if tk == primary then return `self.sources.ptr[0]:[meth]([expr]) else local ok, empty local r = symbol(rt) if tk == ptr then ok = `r.ptr ~= nil empty = `[rt]{ptr=nil,ct=0} elseif tk == stat then ok = `r.ok ~= false empty = `[rt]{ok=false,error=1} elseif tk == simple then ok = `r == true empty = `false end return quote var [r] = empty for i=0,self.sources.ct do var src = self.sources.ptr + i if src.handle ~= nil then r = src:[meth]([expr]) if [ok] then break else r = empty end end end in r end end end) srv.methods.start = terra(self: &srv, befile: rawstring) cfg(self, befile) var success = false for i=0,self.sources.ct do var src = self.sources.ptr + i lib.report('opening data source ', src.id.ptr, '(', src.backend.id, ')') src.handle = src.backend.open(src) if src.handle ~= nil then success = true end end if not success then lib.bail('could not connect to any data sources!') end var dbbind = self:conf_get('bind') var envbind = lib.proc.getenv('parsav_bind') var bind: rawstring if envbind ~= nil then bind = envbind elseif dbbind.ptr ~= nil then bind = dbbind.ptr else bind = '[::]:10917' end lib.report('binding to ', bind) lib.net.mg_mgr_init(&self.webmgr) self.webcon = lib.net.mg_http_listen(&self.webmgr, bind, handle.http, nil) dbbind:free() end srv.methods.poll = terra(self: &srv) lib.net.mg_mgr_poll(&self.webmgr,1000) end srv.methods.shutdown = terra(self: &srv) lib.net.mg_mgr_free(&self.webmgr) for i=0,self.sources.ct do var src = self.sources.ptr + i lib.report('closing data source ', src.id.ptr, '(', src.backend.id, ')') src:close() end self.sources:free() end return srv |
Modified store.t from [a0814e5c61] to [ed1d490f12].
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
-- vim: ft=terra
local m = {}
local backend = {
pgsql = {
};
}
struct m.user {
uid: rawstring
nym: rawstring
handle: rawstring
localuser: bool
}
|
| < > | | > > > | > > > | > > > > | < > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > | | > > > > > > > > > > > > > > | > > > | > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > > |
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 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 |
-- vim: ft=terra
local m = {
timepoint = uint64;
scope = lib.enum {
'public', 'private', 'local';
'personal', 'direct', 'circle';
};
notiftype = lib.enum {
'mention', 'like', 'rt', 'react'
};
relation = lib.enum {
'follow', 'mute', 'block'
};
}
local str = lib.mem.ptr(int8)
str:complete()
struct m.source
struct m.rights {
rank: uint16 -- lower = more powerful except 0 = regular user
-- creating staff automatically assigns rank immediately below you
quota: uint32 -- # of allowed tweets per day; 0 = no limit
-- user powers -- default on
login: bool
visible: bool
post: bool
shout: bool
propagate: bool
upload: bool
-- admin powers -- default off
ban: bool
config: bool
censor: bool
suspend: bool
rebrand: bool -- modify site's brand identity
}
terra m.rights_default()
return m.rights {
rank = 0, quota = 1000;
login = true, visible = true, post = true;
shout = true, propagate = true, upload = true;
ban = false, config = false, censor = false;
suspend = false, rebrand = false;
}
end
struct m.actor {
id: uint64
nym: str
handle: str
origin: uint64
bio: str
rights: m.rights
key: str
source: &m.source
}
terra m.actor:free()
self.nym:free()
self.handle:free()
self.bio:free()
self.key:free()
end
struct m.range {
time: bool
union {
from_time: m.timepoint
from_idx: uint64
}
union {
to_time: m.timepoint
to_idx: uint64
}
}
struct m.post {
id: uint64
author: uint64
subject: str
body: str
posted: m.timepoint
discovered: m.timepoint
scope: m.scope.t
mentions: lib.mem.ptr(uint64)
circles: lib.mem.ptr(uint64) --only meaningful if scope is set to circle
convo: uint64
parent: uint64
source: &m.source
}
local cnf = terralib.memoize(function(ty,rty)
rty = rty or ty
return struct {
enum: {&opaque, uint64, rawstring} -> intptr
get: {&opaque, uint64, rawstring} -> rty
set: {&opaque, uint64, rawstring, ty} -> {}
reset: {&opaque, uint64, rawstring} -> {}
}
end)
struct m.notif {
kind: m.notiftype.t
when: uint64
union {
post: uint64
reaction: int8[8]
}
}
-- backends only handle content on the local server
struct m.backend { id: rawstring
open: &m.source -> &opaque
close: &m.source -> {}
conf_get: {&m.source, rawstring} -> lib.mem.ptr(int8)
conf_set: {&m.source, rawstring, rawstring} -> {}
conf_reset: {&m.source, rawstring} -> {}
actor_save: {&m.source, m.actor} -> bool
actor_create: {&m.source, m.actor} -> bool
actor_fetch_xid: {&m.source, rawstring} -> lib.stat(m.actor)
actor_fetch_uid: {&m.source, uint64} -> lib.stat(m.actor)
actor_notif_fetch_uid: {&m.source, uint64} -> lib.mem.ptr(m.notif)
actor_auth: {&m.source, rawstring, rawstring} -> lib.stat(m.actor)
actor_enum: {&m.source} -> lib.mem.ptr(m.actor)
actor_enum_local: {&m.source} -> lib.mem.ptr(m.actor)
actor_conf_str: cnf(rawstring, lib.mem.ptr(int8))
actor_conf_int: cnf(intptr, lib.stat(intptr))
post_save: {&m.source, &m.post} -> bool
post_create: {&m.source, &m.post} -> bool
actor_post_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(m.post)
convo_fetch_xid: {&m.source,rawstring} -> lib.mem.ptr(m.post)
convo_fetch_uid: {&m.source,uint64} -> lib.mem.ptr(m.post)
actor_timeline_fetch_uid: {&m.source, uint64, m.range} -> lib.mem.ptr(m.post)
instance_timeline_fetch: {&m.source, m.range} -> lib.mem.ptr(m.post)
}
struct m.source {
backend: &m.backend
id: lib.mem.ptr(int8)
handle: &opaque
string: lib.mem.ptr(int8)
}
terra m.source:free()
self.id:free()
self.string:free()
end
m.source.metamethods.__methodmissing = macro(function(meth, obj, ...)
local q = {...}
-- syntax sugar to forward unrecognized calls onto the backend
return `obj.backend.[meth](&obj, [q])
end)
return m
|