|
1 |
| -local json = require "cjson.safe" |
2 |
| -local bit = bit |
3 |
| -local gmatch = string.gmatch |
4 |
| -local ipairs = ipairs |
5 |
| -local tonumber = tonumber |
6 |
| -local byte = string.byte |
7 |
| -local ngx_re_match = ngx.re.match |
8 |
| -local coroutine = coroutine |
9 |
| -local resume = coroutine.resume |
10 |
| -local trace_back = debug.traceback |
11 |
| -local ngx = ngx |
12 |
| -local ngx_header = ngx.header |
13 |
| -local ngx_print = ngx.print |
14 |
| -local ngx_var = ngx.var |
15 |
| -local string_format = string.format |
16 |
| -local xpcall = xpcall |
17 |
| -local spawn = ngx.thread.spawn |
18 |
| -local ngx_req = ngx.req |
19 |
| -local decode = json.decode |
20 |
| -local encode = json.encode |
21 |
| -local get_post_args = ngx.req.get_post_args |
22 |
| -local read_body = ngx.req.read_body |
23 |
| -local get_body_data = ngx.req.get_body_data |
24 |
| -local assert = assert |
25 |
| -local rawget = rawget |
26 |
| -local setmetatable = setmetatable |
| 1 | +local json = require "cjson.safe" |
| 2 | +local bit = bit |
| 3 | +local gmatch = string.gmatch |
| 4 | +local ipairs = ipairs |
| 5 | +local tonumber = tonumber |
| 6 | +local byte = string.byte |
| 7 | +local ngx_re_match = ngx.re.match |
| 8 | +local coroutine = coroutine |
| 9 | +local resume = coroutine.resume |
| 10 | +local trace_back = debug.traceback |
| 11 | +local ngx = ngx |
| 12 | +local ngx_header = ngx.header |
| 13 | +local ngx_print = ngx.print |
| 14 | +local ngx_var = ngx.var |
| 15 | +local string_format = string.format |
| 16 | +local xpcall = xpcall |
| 17 | +local spawn = ngx.thread.spawn |
| 18 | +local ngx_req = ngx.req |
| 19 | +local decode = json.decode |
| 20 | +local encode = json.encode |
| 21 | +local get_post_args = ngx.req.get_post_args |
| 22 | +local read_body = ngx.req.read_body |
| 23 | +local get_body_data = ngx.req.get_body_data |
| 24 | +local assert = assert |
| 25 | +local rawget = rawget |
| 26 | +local setmetatable = setmetatable |
| 27 | +local lfs |
| 28 | +do |
| 29 | + local o, l = pcall(require, "syscall.lfs") |
| 30 | + if not o then o, l = pcall(require, "lfs") end |
| 31 | + if o then lfs = l end |
| 32 | +end |
27 | 33 |
|
28 | 34 | local method_bitmask = {
|
29 | 35 | GET = 1, -- 2^0
|
@@ -201,24 +207,23 @@ function Router:is_route(view)
|
201 | 207 | return self:is_handler(view[2])
|
202 | 208 | end
|
203 | 209 |
|
204 |
| ----init a router with routes |
| 210 | +---insert routes to a router |
205 | 211 | ---@param routes Route[]
|
206 | 212 | ---@return Router
|
207 |
| -function Router:create(routes) |
208 |
| - local tree = Router:new() |
| 213 | +function Router:extend(routes) |
209 | 214 | for _, route in ipairs(routes) do
|
210 | 215 | assert(self:is_route(route))
|
211 | 216 | local path = route[1]
|
212 | 217 | if type(path) == 'string' then
|
213 |
| - tree:_insert(path, route[2], route[3]) |
| 218 | + self:_insert(path, route[2], route[3]) |
214 | 219 | else
|
215 | 220 | for _, p in ipairs(path) do
|
216 |
| - tree:_insert(p, route[2], route[3]) |
| 221 | + self:_insert(p, route[2], route[3]) |
217 | 222 | end
|
218 | 223 | end
|
219 | 224 | end
|
220 |
| - _set_find_method(tree) |
221 |
| - return tree |
| 225 | + _set_find_method(self) |
| 226 | + return self |
222 | 227 | end
|
223 | 228 |
|
224 | 229 | ---@param path string|table
|
@@ -574,4 +579,88 @@ function Router:run()
|
574 | 579 | return self:dispatch(ngx_var.document_uri, ngx_var.request_method)
|
575 | 580 | end
|
576 | 581 |
|
| 582 | +local function callable(handler) |
| 583 | + return type(handler) == "function" or |
| 584 | + (type(handler) == "table" and getmetatable(handler) and getmetatable(handler).__call) |
| 585 | +end |
| 586 | + |
| 587 | +local function is_handler(handler) |
| 588 | + return type(handler) == "string" or callable(handler) |
| 589 | +end |
| 590 | + |
| 591 | +---@param dir string Directory path to scan |
| 592 | +---@param base_path? string Base path (used for recursion) |
| 593 | +---@return table[] Route configuration array |
| 594 | +function Router:collect_routes(dir, base_path) |
| 595 | + local routes = {} |
| 596 | + base_path = base_path or "" |
| 597 | + |
| 598 | + -- Recursively scan directory |
| 599 | + for file in lfs.dir(dir) do |
| 600 | + if file ~= "." and file ~= ".." then |
| 601 | + local path = dir .. "/" .. file |
| 602 | + local attr = lfs.attributes(path) |
| 603 | + |
| 604 | + if attr.mode == "directory" then |
| 605 | + -- Process subdirectories recursively |
| 606 | + local sub_routes = self:collect_routes(path, base_path .. "/" .. file) |
| 607 | + for _, route in ipairs(sub_routes) do |
| 608 | + routes[#routes + 1] = route |
| 609 | + end |
| 610 | + elseif attr.mode == "file" and file:match("%.lua$") then |
| 611 | + -- Process .lua files |
| 612 | + local module_path = path:gsub("%.lua$", ""):gsub("/", ".") |
| 613 | + local ok, route = pcall(require, module_path) |
| 614 | + if ok then |
| 615 | + -- Get relative path (remove .lua extension) |
| 616 | + local relative_path = base_path .. "/" .. file:gsub("%.lua$", "") |
| 617 | + |
| 618 | + -- Handle different types of route definitions |
| 619 | + if is_handler(route) then |
| 620 | + -- Type 1: Function or callable table |
| 621 | + routes[#routes + 1] = { relative_path, route } |
| 622 | + elseif type(route) == "table" then |
| 623 | + if #route > 0 then |
| 624 | + if type(route[1]) == "string" and is_handler(route[2]) then |
| 625 | + -- Type 2: Single route definition |
| 626 | + local path = route[1] |
| 627 | + if path:sub(1, 1) == "/" then |
| 628 | + routes[#routes + 1] = { path, route[2], route[3] } |
| 629 | + else |
| 630 | + routes[#routes + 1] = { relative_path .. "/" .. path, route[2], route[3] } |
| 631 | + end |
| 632 | + else |
| 633 | + -- Type 3: Array of multiple route definitions |
| 634 | + for _, view in ipairs(route) do |
| 635 | + if type(view[1]) == "string" and is_handler(view[2]) then |
| 636 | + if view[1]:sub(1, 1) == "/" then |
| 637 | + routes[#routes + 1] = { view[1], view[2], view[3] } |
| 638 | + else |
| 639 | + routes[#routes + 1] = { relative_path .. "/" .. view[1], view[2], view[3] } |
| 640 | + end |
| 641 | + end |
| 642 | + end |
| 643 | + end |
| 644 | + else |
| 645 | + -- Type 4: Map-style route definitions |
| 646 | + for key, handler in pairs(route) do |
| 647 | + if type(key) == "string" and key:sub(1, 1) ~= "/" and is_handler(handler) then |
| 648 | + routes[#routes + 1] = { relative_path .. "/" .. key, handler } |
| 649 | + end |
| 650 | + end |
| 651 | + end |
| 652 | + end |
| 653 | + end |
| 654 | + end |
| 655 | + end |
| 656 | + end |
| 657 | + |
| 658 | + return routes |
| 659 | +end |
| 660 | + |
| 661 | +function Router:fs(dir) |
| 662 | + local routes = self:collect_routes(dir) |
| 663 | + self:extend(routes) |
| 664 | +end |
| 665 | + |
577 | 666 | return Router
|
0 commit comments