Support IP Hash LB policy
[msb/apigateway.git] / openresty-ext / src / assembly / resources / openresty / nginx / luaext / vendor / pl / utils.lua
1 --- Generally useful routines.
2 -- See  @{01-introduction.md.Generally_useful_functions|the Guide}.
3 --
4 -- Dependencies: `pl.compat`
5 --
6 -- @module pl.utils
7 local format = string.format
8 local compat = require 'pl.compat'
9 local stdout = io.stdout
10 local append = table.insert
11 local unpack = rawget(_G,'unpack') or rawget(table,'unpack')
12
13 local utils = {
14     _VERSION = "1.4.1",
15     lua51 = compat.lua51,
16     setfenv = compat.setfenv,
17     getfenv = compat.getfenv,
18     load = compat.load,
19     execute = compat.execute,
20     dir_separator = _G.package.config:sub(1,1),
21     unpack = unpack
22 }
23
24 --- end this program gracefully.
25 -- @param code The exit code or a message to be printed
26 -- @param ... extra arguments for message's format'
27 -- @see utils.fprintf
28 function utils.quit(code,...)
29     if type(code) == 'string' then
30         utils.fprintf(io.stderr,code,...)
31         code = -1
32     else
33         utils.fprintf(io.stderr,...)
34     end
35     io.stderr:write('\n')
36     os.exit(code)
37 end
38
39 --- print an arbitrary number of arguments using a format.
40 -- @param fmt The format (see string.format)
41 -- @param ... Extra arguments for format
42 function utils.printf(fmt,...)
43     utils.assert_string(1,fmt)
44     utils.fprintf(stdout,fmt,...)
45 end
46
47 --- write an arbitrary number of arguments to a file using a format.
48 -- @param f File handle to write to.
49 -- @param fmt The format (see string.format).
50 -- @param ... Extra arguments for format
51 function utils.fprintf(f,fmt,...)
52     utils.assert_string(2,fmt)
53     f:write(format(fmt,...))
54 end
55
56 local function import_symbol(T,k,v,libname)
57     local key = rawget(T,k)
58     -- warn about collisions!
59     if key and k ~= '_M' and k ~= '_NAME' and k ~= '_PACKAGE' and k ~= '_VERSION' then
60         utils.fprintf(io.stderr,"warning: '%s.%s' will not override existing symbol\n",libname,k)
61         return
62     end
63     rawset(T,k,v)
64 end
65
66 local function lookup_lib(T,t)
67     for k,v in pairs(T) do
68         if v == t then return k end
69     end
70     return '?'
71 end
72
73 local already_imported = {}
74
75 --- take a table and 'inject' it into the local namespace.
76 -- @param t The Table
77 -- @param T An optional destination table (defaults to callers environment)
78 function utils.import(t,T)
79     T = T or _G
80     t = t or utils
81     if type(t) == 'string' then
82         t = require (t)
83     end
84     local libname = lookup_lib(T,t)
85     if already_imported[t] then return end
86     already_imported[t] = libname
87     for k,v in pairs(t) do
88         import_symbol(T,k,v,libname)
89     end
90 end
91
92 utils.patterns = {
93     FLOAT = '[%+%-%d]%d*%.?%d*[eE]?[%+%-]?%d*',
94     INTEGER = '[+%-%d]%d*',
95     IDEN = '[%a_][%w_]*',
96     FILE = '[%a%.\\][:%][%w%._%-\\]*'
97 }
98
99 --- escape any 'magic' characters in a string
100 -- @param s The input string
101 function utils.escape(s)
102     utils.assert_string(1,s)
103     return (s:gsub('[%-%.%+%[%]%(%)%$%^%%%?%*]','%%%1'))
104 end
105
106 --- return either of two values, depending on a condition.
107 -- @param cond A condition
108 -- @param value1 Value returned if cond is true
109 -- @param value2 Value returned if cond is false (can be optional)
110 function utils.choose(cond,value1,value2)
111     if cond then return value1
112     else return value2
113     end
114 end
115
116 local raise
117
118 --- return the contents of a file as a string
119 -- @param filename The file path
120 -- @param is_bin open in binary mode
121 -- @return file contents
122 function utils.readfile(filename,is_bin)
123     local mode = is_bin and 'b' or ''
124     utils.assert_string(1,filename)
125     local f,open_err = io.open(filename,'r'..mode)
126     if not f then return utils.raise (open_err) end
127     local res,read_err = f:read('*a')
128     f:close()
129     if not res then
130         -- Errors in io.open have "filename: " prefix,
131         -- error in file:read don't, add it.
132         return raise (filename..": "..read_err)
133     end
134     return res
135 end
136
137 --- write a string to a file
138 -- @param filename The file path
139 -- @param str The string
140 -- @param is_bin open in binary mode
141 -- @return true or nil
142 -- @return error message
143 -- @raise error if filename or str aren't strings
144 function utils.writefile(filename,str,is_bin)
145     local mode = is_bin and 'b' or ''
146     utils.assert_string(1,filename)
147     utils.assert_string(2,str)
148     local f,err = io.open(filename,'w'..mode)
149     if not f then return raise(err) end
150     f:write(str)
151     f:close()
152     return true
153 end
154
155 --- return the contents of a file as a list of lines
156 -- @param filename The file path
157 -- @return file contents as a table
158 -- @raise errror if filename is not a string
159 function utils.readlines(filename)
160     utils.assert_string(1,filename)
161     local f,err = io.open(filename,'r')
162     if not f then return raise(err) end
163     local res = {}
164     for line in f:lines() do
165         append(res,line)
166     end
167     f:close()
168     return res
169 end
170
171 --- split a string into a list of strings separated by a delimiter.
172 -- @param s The input string
173 -- @param re A Lua string pattern; defaults to '%s+'
174 -- @param plain don't use Lua patterns
175 -- @param n optional maximum number of splits
176 -- @return a list-like table
177 -- @raise error if s is not a string
178 function utils.split(s,re,plain,n)
179     utils.assert_string(1,s)
180     local find,sub,append = string.find, string.sub, table.insert
181     local i1,ls = 1,{}
182     if not re then re = '%s+' end
183     if re == '' then return {s} end
184     while true do
185         local i2,i3 = find(s,re,i1,plain)
186         if not i2 then
187             local last = sub(s,i1)
188             if last ~= '' then append(ls,last) end
189             if #ls == 1 and ls[1] == '' then
190                 return {}
191             else
192                 return ls
193             end
194         end
195         append(ls,sub(s,i1,i2-1))
196         if n and #ls == n then
197             ls[#ls] = sub(s,i1)
198             return ls
199         end
200         i1 = i3+1
201     end
202 end
203
204 --- split a string into a number of values.
205 -- @param s the string
206 -- @param re the delimiter, default space
207 -- @return n values
208 -- @usage first,next = splitv('jane:doe',':')
209 -- @see split
210 function utils.splitv (s,re)
211     return unpack(utils.split(s,re))
212 end
213
214 --- convert an array of values to strings.
215 -- @param t a list-like table
216 -- @param temp buffer to use, otherwise allocate
217 -- @param tostr custom tostring function, called with (value,index).
218 -- Otherwise use `tostring`
219 -- @return the converted buffer
220 function utils.array_tostring (t,temp,tostr)
221     temp, tostr = temp or {}, tostr or tostring
222     for i = 1,#t do
223         temp[i] = tostr(t[i],i)
224     end
225     return temp
226 end
227
228 local is_windows = package.config:sub(1, 1) == "\\"
229
230 --- Quote an argument of a command.
231 -- Quotes a single argument of a command to be passed
232 -- to `os.execute`, `pl.utils.execute` or `pl.utils.executeex`.
233 -- @string argument the argument.
234 -- @return quoted argument.
235 function utils.quote_arg(argument)
236     if is_windows then
237         if argument == "" or argument:find('[ \f\t\v]') then
238             -- Need to quote the argument.
239             -- Quotes need to be escaped with backslashes;
240             -- additionally, backslashes before a quote, escaped or not,
241             -- need to be doubled.
242             -- See documentation for CommandLineToArgvW Windows function.
243             argument = '"' .. argument:gsub([[(\*)"]], [[%1%1\"]]):gsub([[\+$]], "%0%0") .. '"'
244         end
245
246         -- os.execute() uses system() C function, which on Windows passes command
247         -- to cmd.exe. Escape its special characters.
248         return (argument:gsub('["^<>!|&%%]', "^%0"))
249     else
250         if argument == "" or argument:find('[^a-zA-Z0-9_@%+=:,./-]') then
251             -- To quote arguments on posix-like systems use single quotes.
252             -- To represent an embedded single quote close quoted string ('),
253             -- add escaped quote (\'), open quoted string again (').
254             argument = "'" .. argument:gsub("'", [['\'']]) .. "'"
255         end
256
257         return argument
258     end
259 end
260
261 --- execute a shell command and return the output.
262 -- This function redirects the output to tempfiles and returns the content of those files.
263 -- @param cmd a shell command
264 -- @param bin boolean, if true, read output as binary file
265 -- @return true if successful
266 -- @return actual return code
267 -- @return stdout output (string)
268 -- @return errout output (string)
269 function utils.executeex(cmd, bin)
270     local mode
271     local outfile = os.tmpname()
272     local errfile = os.tmpname()
273
274     if is_windows and not outfile:find(':') then
275         outfile = os.getenv('TEMP')..outfile
276         errfile = os.getenv('TEMP')..errfile
277     end
278     cmd = cmd .. " > " .. utils.quote_arg(outfile) .. " 2> " .. utils.quote_arg(errfile)
279
280     local success, retcode = utils.execute(cmd)
281     local outcontent = utils.readfile(outfile, bin)
282     local errcontent = utils.readfile(errfile, bin)
283     os.remove(outfile)
284     os.remove(errfile)
285     return success, retcode, (outcontent or ""), (errcontent or "")
286 end
287
288 --- 'memoize' a function (cache returned value for next call).
289 -- This is useful if you have a function which is relatively expensive,
290 -- but you don't know in advance what values will be required, so
291 -- building a table upfront is wasteful/impossible.
292 -- @param func a function of at least one argument
293 -- @return a function with at least one argument, which is used as the key.
294 function utils.memoize(func)
295     local cache = {}
296     return function(k)
297         local res = cache[k]
298         if res == nil then
299             res = func(k)
300             cache[k] = res
301         end
302         return res
303     end
304 end
305
306
307 utils.stdmt = {
308     List = {_name='List'}, Map = {_name='Map'},
309     Set = {_name='Set'}, MultiMap = {_name='MultiMap'}
310 }
311
312 local _function_factories = {}
313
314 --- associate a function factory with a type.
315 -- A function factory takes an object of the given type and
316 -- returns a function for evaluating it
317 -- @tab mt metatable
318 -- @func fun a callable that returns a function
319 function utils.add_function_factory (mt,fun)
320     _function_factories[mt] = fun
321 end
322
323 local function _string_lambda(f)
324     local raise = utils.raise
325     if f:find '^|' or f:find '_' then
326         local args,body = f:match '|([^|]*)|(.+)'
327         if f:find '_' then
328             args = '_'
329             body = f
330         else
331             if not args then return raise 'bad string lambda' end
332         end
333         local fstr = 'return function('..args..') return '..body..' end'
334         local fn,err = utils.load(fstr)
335         if not fn then return raise(err) end
336         fn = fn()
337         return fn
338     else return raise 'not a string lambda'
339     end
340 end
341
342 --- an anonymous function as a string. This string is either of the form
343 -- '|args| expression' or is a function of one argument, '_'
344 -- @param lf function as a string
345 -- @return a function
346 -- @usage string_lambda '|x|x+1' (2) == 3
347 -- @usage string_lambda '_+1' (2) == 3
348 -- @function utils.string_lambda
349 utils.string_lambda = utils.memoize(_string_lambda)
350
351 local ops
352
353 --- process a function argument.
354 -- This is used throughout Penlight and defines what is meant by a function:
355 -- Something that is callable, or an operator string as defined by <code>pl.operator</code>,
356 -- such as '>' or '#'. If a function factory has been registered for the type, it will
357 -- be called to get the function.
358 -- @param idx argument index
359 -- @param f a function, operator string, or callable object
360 -- @param msg optional error message
361 -- @return a callable
362 -- @raise if idx is not a number or if f is not callable
363 function utils.function_arg (idx,f,msg)
364     utils.assert_arg(1,idx,'number')
365     local tp = type(f)
366     if tp == 'function' then return f end  -- no worries!
367     -- ok, a string can correspond to an operator (like '==')
368     if tp == 'string' then
369         if not ops then ops = require 'pl.operator'.optable end
370         local fn = ops[f]
371         if fn then return fn end
372         local fn, err = utils.string_lambda(f)
373         if not fn then error(err..': '..f) end
374         return fn
375     elseif tp == 'table' or tp == 'userdata' then
376         local mt = getmetatable(f)
377         if not mt then error('not a callable object',2) end
378         local ff = _function_factories[mt]
379         if not ff then
380             if not mt.__call then error('not a callable object',2) end
381             return f
382         else
383             return ff(f) -- we have a function factory for this type!
384         end
385     end
386     if not msg then msg = " must be callable" end
387     if idx > 0 then
388         error("argument "..idx..": "..msg,2)
389     else
390         error(msg,2)
391     end
392 end
393
394 --- bind the first argument of the function to a value.
395 -- @param fn a function of at least two values (may be an operator string)
396 -- @param p a value
397 -- @return a function such that f(x) is fn(p,x)
398 -- @raise same as @{function_arg}
399 -- @see func.bind1
400 function utils.bind1 (fn,p)
401     fn = utils.function_arg(1,fn)
402     return function(...) return fn(p,...) end
403 end
404
405 --- bind the second argument of the function to a value.
406 -- @param fn a function of at least two values (may be an operator string)
407 -- @param p a value
408 -- @return a function such that f(x) is fn(x,p)
409 -- @raise same as @{function_arg}
410 function utils.bind2 (fn,p)
411     fn = utils.function_arg(1,fn)
412     return function(x,...) return fn(x,p,...) end
413 end
414
415
416 --- assert that the given argument is in fact of the correct type.
417 -- @param n argument index
418 -- @param val the value
419 -- @param tp the type
420 -- @param verify an optional verification function
421 -- @param msg an optional custom message
422 -- @param lev optional stack position for trace, default 2
423 -- @raise if the argument n is not the correct type
424 -- @usage assert_arg(1,t,'table')
425 -- @usage assert_arg(n,val,'string',path.isdir,'not a directory')
426 function utils.assert_arg (n,val,tp,verify,msg,lev)
427     if type(val) ~= tp then
428         error(("argument %d expected a '%s', got a '%s'"):format(n,tp,type(val)),lev or 2)
429     end
430     if verify and not verify(val) then
431         error(("argument %d: '%s' %s"):format(n,val,msg),lev or 2)
432     end
433 end
434
435 --- assert the common case that the argument is a string.
436 -- @param n argument index
437 -- @param val a value that must be a string
438 -- @raise val must be a string
439 function utils.assert_string (n,val)
440     utils.assert_arg(n,val,'string',nil,nil,3)
441 end
442
443 local err_mode = 'default'
444
445 --- control the error strategy used by Penlight.
446 -- Controls how <code>utils.raise</code> works; the default is for it
447 -- to return nil and the error string, but if the mode is 'error' then
448 -- it will throw an error. If mode is 'quit' it will immediately terminate
449 -- the program.
450 -- @param mode - either 'default', 'quit'  or 'error'
451 -- @see utils.raise
452 function utils.on_error (mode)
453     if ({['default'] = 1, ['quit'] = 2, ['error'] = 3})[mode] then
454       err_mode = mode
455     else
456       -- fail loudly
457       if err_mode == 'default' then err_mode = 'error' end
458       utils.raise("Bad argument expected string; 'default', 'quit', or 'error'. Got '"..tostring(mode).."'")
459     end
460 end
461
462 --- used by Penlight functions to return errors.  Its global behaviour is controlled
463 -- by <code>utils.on_error</code>
464 -- @param err the error string.
465 -- @see utils.on_error
466 function utils.raise (err)
467     if err_mode == 'default' then return nil,err
468     elseif err_mode == 'quit' then utils.quit(err)
469     else error(err,2)
470     end
471 end
472
473 --- is the object of the specified type?.
474 -- If the type is a string, then use type, otherwise compare with metatable
475 -- @param obj An object to check
476 -- @param tp String of what type it should be
477 function utils.is_type (obj,tp)
478     if type(tp) == 'string' then return type(obj) == tp end
479     local mt = getmetatable(obj)
480     return tp == mt
481 end
482
483 raise = utils.raise
484
485 --- load a code string or bytecode chunk.
486 -- @param code Lua code as a string or bytecode
487 -- @param name for source errors
488 -- @param mode kind of chunk, 't' for text, 'b' for bytecode, 'bt' for all (default)
489 -- @param env  the environment for the new chunk (default nil)
490 -- @return compiled chunk
491 -- @return error message (chunk is nil)
492 -- @function utils.load
493
494 ---------------
495 -- Get environment of a function.
496 -- With Lua 5.2, may return nil for a function with no global references!
497 -- Based on code by [Sergey Rozhenko](http://lua-users.org/lists/lua-l/2010-06/msg00313.html)
498 -- @param f a function or a call stack reference
499 -- @function utils.getfenv
500
501 ---------------
502 -- Set environment of a function
503 -- @param f a function or a call stack reference
504 -- @param env a table that becomes the new environment of `f`
505 -- @function utils.setfenv
506
507 --- execute a shell command.
508 -- This is a compatibility function that returns the same for Lua 5.1 and Lua 5.2
509 -- @param cmd a shell command
510 -- @return true if successful
511 -- @return actual return code
512 -- @function utils.execute
513
514 return utils
515
516