summaryrefslogtreecommitdiff
path: root/indra/newview/scripts/lua/require/result_view.lua
blob: 5301d7838c9eca783d5739b73df8914a95a40bac (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
local leap = require 'leap'

-- metatable for every result_view() table
local mt = {
    __len = function(self)
        return self.length
    end,
    __index = function(self, i)
        -- right away, convert to 0-relative indexing
        i -= 1
        -- can we find this index within the current slice?
        local reli = i - self.start
        if 0 <= reli and reli < #self.slice then
            -- Lua 1-relative indexing
            return self.slice[reli + 1]
        end
        -- is this index outside the overall result set?
        if not (0 <= i and i < self.length) then
            return nil
        end
        -- fetch a new slice starting at i, using provided fetch()
        local start
        self.slice, start = self.fetch(self.key, i)
        -- It's possible that caller-provided fetch() function forgot
        -- to return the adjusted start index of the new slice. In
        -- Lua, 0 tests as true, so if fetch() returned (slice, 0),
        -- we'll duly reset self.start to 0. Otherwise, assume the
        -- requested index was not adjusted: that the returned slice
        -- really does start at i.
        self.start = start or i
        -- Hopefully this slice contains the desired i.
        -- Back to 1-relative indexing.
        return self.slice[i - self.start + 1]
    end,
    -- We purposely avoid putting any array entries (int keys) into
    -- our table so that access to any int key will always call our
    -- __index() metamethod. Moreover, we want any table iteration to
    -- call __index(table, i) however many times; we do NOT want it to
    -- retrieve key, length, start, slice.
    -- So turn 'for k, v in result' into 'for k, v in ipairs(result)'.
    __iter = ipairs,
    -- This result set provides read-only access.
    -- We do not support pushing updates to individual items back to
    -- C++; for the intended use cases, that makes no sense.
    __newindex = function(self, i, value)
        error("result_view is a read-only data structure", 2)
    end
}

-- result_view(key_length, fetch) returns a table which stores only a slice
-- of a result set plus some control values, yet presents read-only virtual
-- access to the entire result set.
-- key_length: {result set key, total result set length}
-- fetch:      function(key, start) that returns (slice, adjusted start) 
local result_view = setmetatable(
    {
        -- generic fetch() function
        fetch = function(key, start)
            local fetched = leap.request(
                'LLInventory',
                {op='getSlice', result=key, index=start})
            return fetched.slice, fetched.start
        end,
        -- generic close() function accepting variadic result-set keys
        close = function(...)
            local keys = table.pack(...)
            -- table.pack() produces a table with an array entry for every
            -- parameter, PLUS an 'n' key with the count. Unfortunately that
            -- 'n' key bollixes our conversion to LLSD, which requires either
            -- all int keys (for an array) or all string keys (for a map).
            keys.n = nil
            leap.send('LLInventory', {op='closeResult', result=keys})
        end
    },
    {
        -- result_view(key_length, fetch) calls this
        __call = function(class, key_length, fetch)
            return setmetatable(
                {
                    key=key_length[1],
                    length=key_length[2],
                    -- C++ result sets use 0-based indexing, so internally we do too
                    start=0,
                    -- start with a dummy array with length 0
                    slice={},
                    -- if caller didn't pass fetch() function, use generic
                    fetch=fetch or class.fetch,
                    -- returned view:close() will close result set with passed key
                    close=function(self) class.close(key_length[1]) end
                },
                -- use our special metatable
                mt
            )
        end
    }
)

return result_view