summaryrefslogtreecommitdiff
path: root/script-beta/proto/proto.lua
blob: f04653d5f9871f03c02dfc4a57f20ac22cccdfb6 (plain)
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
local subprocess = require 'bee.subprocess'
local util       = require 'utility'
local await      = require 'await'
local pub        = require 'pub'
local jsonrpc    = require 'jsonrpc'
local ErrorCodes = require 'define.ErrorCodes'

local reqCounter = util.counter()

local m = {}

m.ability = {}
m.waiting = {}

function m.getMethodName(proto)
    if proto.method:sub(1, 2) == '$/' then
        return proto.method:sub(3), true
    else
        return proto.method, false
    end
end

function m.on(method, callback)
    m.ability[method] = callback
end

function m.response(id, res)
    if id == nil then
        log.error('Response id is nil!', util.dump(res))
        return
    end
    -- res 可能是nil,为了转成json时保留nil,使用 container 容器
    local data = util.container()
    data.id     = id
    data.result = res
    local buf = jsonrpc.encode(data)
    log.debug('Response', id, #buf)
    io.stdout:write(buf)
end

function m.responseErr(id, code, message)
    if id == nil then
        log.error('Response id is nil!', util.dump(message))
        return
    end
    local buf = jsonrpc.encode {
        id    = id,
        error = {
            code    = code,
            message = message,
        }
    }
    log.debug('ResponseErr', id, #buf)
    io.stdout:write(buf)
end

function m.notify(name, params)
    local buf = jsonrpc.encode {
        method = name,
        params = params,
    }
    log.debug('Notify', name, #buf)
    io.stdout:write(buf)
end

function m.awaitRequest(name, params)
    local id = reqCounter()
    local buf = jsonrpc.encode {
        id     = id,
        method = name,
        params = params,
    }
    log.debug('Request', name, #buf)
    io.stdout:write(buf)
    return await.wait(function (waker)
        m.waiting[id] = waker
    end)
end

function m.doMethod(proto)
    local method, optional = m.getMethodName(proto)
    local abil = m.ability[method]
    if not abil then
        if not optional then
            log.warn('Recieved unknown proto: ' .. method)
        end
        if proto.id then
            m.responseErr(proto.id, ErrorCodes.MethodNotFound, method)
        end
        return
    end
    await.create(function ()
        local clock = os.clock()
        local ok, res = xpcall(abil, log.error, proto.params)
        local passed = os.clock() - clock
        if passed > 0.2 then
            log.debug(('Method [%s] takes [%.3f]sec.'):format(method, passed))
        end
        if not proto.id then
            return
        end
        if ok then
            m.response(proto.id, res)
        else
            m.responseErr(proto.id, ErrorCodes.InternalError, res)
        end
    end)
end

function m.doResponse(proto)
    local id = proto.id
    local waker = m.waiting[id]
    if not waker then
        log.warn('Response id not found: ' .. util.dump(proto))
        return
    end
    m.waiting[id] = nil
    if proto.error then
        log.warn(('Response error [%d]: %s'):format(proto.error.code, proto.error.message))
        return
    end
    waker(proto.result)
end

function m.listen()
    subprocess.filemode(io.stdin,  'b')
    subprocess.filemode(io.stdout, 'b')
    io.stdin:setvbuf  'no'
    io.stdout:setvbuf 'no'
    pub.task('loadProto')
end

return m