├── .busted ├── .github └── workflows │ └── unit-tests.yaml ├── .gitignore ├── .luacheckrc ├── .luacov ├── CHANGES.md ├── CMakeLists.txt ├── LICENSE ├── Makefile ├── README.md ├── config.ld ├── dev └── Dockerfile ├── dist.ini ├── docs ├── classes │ ├── search.html │ └── url.html ├── data │ └── icon.png ├── index.html ├── ldoc_new.css └── modules │ ├── resty.ada.html │ └── resty.ada.search.html ├── lib └── resty │ ├── ada.lua │ └── ada │ ├── lib.lua │ ├── search.lua │ └── utils.lua ├── lua-resty-ada-1.1.0-1.rockspec └── spec └── 01-ada-url_spec.lua /.busted: -------------------------------------------------------------------------------- 1 | return { 2 | default = { 3 | lua = "resty", 4 | verbose = true, 5 | coverage = true, 6 | output = "gtest", 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /.github/workflows/unit-tests.yaml: -------------------------------------------------------------------------------- 1 | name: unit-tests 2 | on: [pull_request] 3 | 4 | jobs: 5 | test: 6 | strategy: 7 | fail-fast: false 8 | matrix: 9 | luaVersion: ["luajit-openresty"] 10 | 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/checkout@v4 14 | 15 | - name: Setup environment 16 | run: docker build dev/ -t resty-ada 17 | 18 | - name: Run tests 19 | run: docker run --network=host -v $PWD:/test -w /test resty-ada bash -c "make test" 20 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | /.project 2 | -------------------------------------------------------------------------------- /.luacheckrc: -------------------------------------------------------------------------------- 1 | std = "ngx_lua" 2 | redefined = false 3 | max_line_length = false 4 | -------------------------------------------------------------------------------- /.luacov: -------------------------------------------------------------------------------- 1 | return { 2 | -- filename to store stats collected 3 | ["statsfile"] = "luacov.stats.out", 4 | 5 | -- filename to store report 6 | ["reportfile"] = "luacov.report.out", 7 | 8 | -- Patterns for files to include when reporting 9 | -- all will be included if nothing is listed 10 | -- (exclude overrules include, do not include 11 | -- the .lua extension, path separator is always '/') 12 | ["include"] = { 'resty/ada' }, 13 | 14 | -- Patterns for files to exclude when reporting 15 | -- all will be included if nothing is listed 16 | -- (exclude overrules include, do not include 17 | -- the .lua extension, path separator is always '/') 18 | ["exclude"] = { 19 | "luacov$", 20 | "luacov/reporter$", 21 | "luacov/defaults$", 22 | "luacov/runner$", 23 | "luacov/stats$", 24 | "luacov/tick$", 25 | }, 26 | } 27 | -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | All notable changes to `lua-resty-ada` will be documented in this file. 4 | 5 | ## [1.2.0] - Unreleased 6 | ### Changed 7 | - Bumped Ada to `3.2.1` 8 | 9 | ## [1.1.0] - 2024-09-03 10 | ### Added 11 | - `url:decode` method 12 | - `ada.decode` function 13 | - `ada.search_encode` function 14 | - `ada.search.encode` function 15 | - `search:decode` method 16 | - `search:decode_all` method 17 | - `ada.search_decode` function 18 | - `ada.search.decode` function 19 | - `ada.search_decode_all` function 20 | - `ada.search.decode_all` function 21 | ### Changed 22 | - The `set_port` to not allow negative or positive inf or NaN 23 | ### Updated 24 | - The CI is now executed against Ada 2.9.2 25 | 26 | ## [1.0.1] - 2024-08-20 27 | ### Removed 28 | - The unnecessary `:is_valid` was removed (the URL is validated when parsed) 29 | ### Added 30 | - Explicitly free Ada URL object on invalid URLs 31 | 32 | ## [1.0.0] - 2024-08-08 33 | ### Added 34 | - Initial, but complete, implementation of LuaJIT FFI bindings to Ada 35 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.16) 2 | 3 | project( 4 | ada 5 | DESCRIPTION "Fast spec-compliant URL parser" 6 | LANGUAGES C CXX 7 | VERSION 3.2.1 8 | ) 9 | 10 | set(CMAKE_CXX_STANDARD 20) 11 | 12 | # Avoid warning about DOWNLOAD_EXTRACT_TIMESTAMP in CMake 3.24: 13 | if (CMAKE_VERSION VERSION_GREATER_EQUAL "3.24.0") 14 | cmake_policy(SET CMP0135 NEW) 15 | endif() 16 | 17 | include(FetchContent) 18 | 19 | FetchContent_Declare( 20 | ada 21 | URL https://github.com/ada-url/ada/releases/download/v3.2.1/singleheader.zip 22 | URL_HASH SHA256=2954ff2208aa016de4213af7371273e1c41c71571e373eadf550ada808c79f42 23 | ) 24 | 25 | FetchContent_MakeAvailable(ada) 26 | 27 | add_library(ada SHARED _deps/ada-src/ada.cpp) 28 | 29 | install(TARGETS ada DESTINATION lib) 30 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024–2025 Aapo Talvensaari, 2024 Guilherme Salazar 2 | All rights reserved. 3 | 4 | Redistribution and use in source and binary forms, with or without 5 | modification, are permitted provided that the following conditions are met: 6 | 7 | * Redistributions of source code must retain the above copyright notice, this 8 | list of conditions and the following disclaimer. 9 | 10 | * Redistributions in binary form must reproduce the above copyright notice, 11 | this list of conditions and the following disclaimer in the documentation 12 | and/or other materials provided with the distribution. 13 | 14 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 15 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 17 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 18 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 20 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 21 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 22 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 23 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 24 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: build copy-library luarocks-install lint unit coverage test docs clean install-rock install-lib install 2 | 3 | LIBRARY := libada.so 4 | ifeq ($(shell uname), Darwin) 5 | LIBRARY := libada.dylib 6 | endif 7 | 8 | build: 9 | @cmake -B build 10 | @cmake --build build 11 | 12 | copy-library: build 13 | @cp build/$(LIBRARY) . 14 | 15 | luarocks-install: 16 | @luarocks make 17 | 18 | lint: 19 | @luacheck ./lib 20 | 21 | unit: 22 | @busted 23 | 24 | coverage: unit 25 | @luacov 26 | @echo 27 | @awk '/File/,0' luacov.report.out 28 | @echo 29 | 30 | test: copy-library luarocks-install coverage lint 31 | 32 | docs: 33 | @ldoc . 34 | 35 | install-rock: 36 | @luarocks make 37 | 38 | install-lib: build 39 | @cmake --install build 40 | 41 | install: install-rock install-lib 42 | 43 | clean: 44 | @rm -Rf build luacov.stats.out luacov.report.out $(LIBRARY) 45 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # lua-resty-ada 2 | 3 | **lua-resty-ada** implements a LuaJIT FFI bindings to 4 | [Ada — WHATWG-compliant and fast URL parser](https://github.com/ada-url/ada/). 5 | 6 | ## Status 7 | 8 | This library is considered production ready. 9 | 10 | 11 | ## Synopsis 12 | 13 | ```lua 14 | local ada = require("resty.ada") 15 | 16 | local url = assert(ada.parse("https://www.7‑Eleven.com:1234/Home/../Privacy/Montréal")) 17 | 18 | print(tostring(url)) 19 | -- prints: https://www.xn--7eleven-506c.com:1234/Privacy/Montr%C3%A9al 20 | 21 | print(tostring(url:clear_port())) -- there are many more methods 22 | -- prints: https://www.xn--7eleven-506c.com/Privacy/Montr%C3%A9al 23 | 24 | url:free() 25 | -- explicitly frees the memory without waiting for the garbage collector 26 | 27 | -- There is also a static API 28 | 29 | print(ada.get_href("https://www.7‑Eleven.com:1234/Home/../Privacy/Montréal")) 30 | -- prints: https://www.xn--7eleven-506c.com:1234/Privacy/Montr%C3%A9al 31 | 32 | print(ada.clear_port("https://www.7‑Eleven.com:1234/Home/../Privacy/Montréal")) 33 | -- prints: https://www.xn--7eleven-506c.com/Privacy/Montr%C3%A9al 34 | ``` 35 | 36 | 37 | ## API 38 | 39 | LDoc generated API docs can be viewed at [bungle.github.io/lua-resty-ada](https://bungle.github.io/lua-resty-ada/). 40 | 41 | 42 | ## Installation 43 | 44 | ### Using OpenResty Package Manager 45 | 46 | ```bash 47 | ❯ opm get bungle/lua-resty-ada 48 | ``` 49 | 50 | OPM repository for `lua-resty-ada` is located at 51 | [opm.openresty.org/package/bungle/lua-resty-ada](https://opm.openresty.org/package/bungle/lua-resty-ada/). 52 | 53 | ### Using LuaRocks 54 | 55 | ```bash 56 | ❯ luarocks install lua-resty-ada 57 | ``` 58 | 59 | LuaRocks repository for `lua-resty-ada` is located at 60 | [luarocks.org/modules/bungle/lua-resty-ada](https://luarocks.org/modules/bungle/lua-resty-session). 61 | 62 | ### Building Ada 63 | 64 | Please consult [Ada](https://github.com/ada-url/ada/) on how to build or install 65 | the ada library. The Ada library needs to installed in in the system library path or 66 | one of the paths in Lua's `package.cpath`. 67 | 68 | This project can also build it by executing (requires [cmake](https://cmake.org/)): 69 | 70 | ```bash 71 | ❯ make build 72 | ``` 73 | 74 | Or run the test suite with [act](https://github.com/nektos/act): 75 | 76 | ```bash 77 | ❯ act 78 | ``` 79 | 80 | 81 | # License 82 | 83 | `lua-resty-ada` uses two clause BSD license. 84 | 85 | ``` 86 | Copyright (c) 2024–2025 Aapo Talvensaari, 2024 Guilherme Salazar 87 | All rights reserved. 88 | 89 | Redistribution and use in source and binary forms, with or without modification, 90 | are permitted provided that the following conditions are met: 91 | 92 | * Redistributions of source code must retain the above copyright notice, this 93 | list of conditions and the following disclaimer. 94 | 95 | * Redistributions in binary form must reproduce the above copyright notice, this 96 | list of conditions and the following disclaimer in the documentation and/or 97 | other materials provided with the distribution. 98 | 99 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND 100 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 101 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 102 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR 103 | ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 104 | ``` 105 | -------------------------------------------------------------------------------- /config.ld: -------------------------------------------------------------------------------- 1 | project = "resty.ada" 2 | description = "WHATWG-compliant and fast URL parser" 3 | full_description = "`lua-resty-ada` implements a LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser" 4 | title = "LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser" 5 | dir = "docs" 6 | use_markdown_titles = true 7 | package = "ada" 8 | format = "discount" 9 | file = "./lib/resty" 10 | style = "!new" 11 | multimodule = true 12 | icon = "icon.png" 13 | -------------------------------------------------------------------------------- /dev/Dockerfile: -------------------------------------------------------------------------------- 1 | FROM openresty/openresty:1.27.1.1-0-noble 2 | 3 | ENV DEBIAN_FRONTEND noninteractive 4 | ENV TEST_NGINX_BINARY openresty 5 | 6 | USER root 7 | 8 | RUN apt update 9 | RUN apt install -y gcc git cmake 10 | RUN luarocks install luacheck 11 | RUN luarocks install busted 12 | RUN luarocks install LuaCov 13 | -------------------------------------------------------------------------------- /dist.ini: -------------------------------------------------------------------------------- 1 | name = lua-resty-ada 2 | abstract = LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser 3 | author = Aapo Talvensaari (@bungle) 4 | is_original = yes 5 | license = 2bsd 6 | repo_link = https://github.com/bungle/lua-resty-ada 7 | requires = luajit 8 | -------------------------------------------------------------------------------- /docs/classes/search.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 61 | 62 |
63 | 64 |

Class search

65 |

Ada URL search parameters

66 |

See: https://url.spec.whatwg.org/#interface-urlsearchparams

67 | 68 | 69 |

Decode Methods

70 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 |
search:decode ()Decodes search parameters and returns a Lua table of them.
search:decode ()Decodes all search parameters and returns a Lua table of them.
80 |

Has Methods

81 | 82 | 83 | 84 | 85 | 86 | 87 | 88 | 89 | 90 |
search:has (key)Checks whether the search has a key.
search:has_value (key, value)Checks whether the search has a key with a specific value.
91 |

Get Methods

92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 |
search:get (key)Get search parameter’s value.
search:get_all (key)Get all the search parameter’s values.
102 |

Set Methods

103 | 104 | 105 | 106 | 107 | 108 | 109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 |
search:reset (search)Sets (or resets) the search parameters.
search:set (key, value)Set the search parameter’s value.
search:append (key, value)Append value to the the search parameter.
117 |

Other Methods

118 | 119 | 120 | 121 | 122 | 123 | 124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 |
search:tostring ()Return search parameters as a string.
search:sort ()Sort search parameters.
search:size ()Count search parameters.
132 |

Meta Methods

133 | 134 | 135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 |
search:__pairs ()Iterate over each key and value in search parameters.
search:__pairs ()Iterate over each parameter in search parameters.
search:__tostring ()Return search parameters as a string.
search:__len ()Count search parameters.
151 |

Destructor Method

152 | 153 | 154 | 155 | 156 | 157 |
search:free ()Explicitly destroys the Ada URL Search instance and frees the memory.
158 |

Remove Methods

159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 | 168 |
search:remove (key)Remove search parameter.
search:remove_value (key, value)Remove search parameter’s value.
169 |

Iterate Methods

170 | 171 | 172 | 173 | 174 | 175 | 176 | 177 | 178 | 179 | 180 | 181 | 182 | 183 | 184 | 185 | 186 | 187 | 188 | 189 | 190 | 191 |
search:each ()Iterate over search parameters.
search:each_key ()Iterate over each key in search parameters.
search:each_value ()Iterate over each value in search parameters.
search:pairs ()Iterate over each key and value in search parameters.
search:ipairs ()Iterate over each parameter in search parameters.
192 | 193 |
194 |
195 | 196 | 197 |

Decode Methods

198 | 199 |
200 |
201 | 202 | search:decode () 203 |
204 |
205 | Decodes search parameters and returns a Lua table of them.

206 | 207 |

If same parameter appears multiple times, only the value of the 208 | first is returned.

209 | 210 |

An example return value: 211 | { 212 | key1 = “value”, 213 | key2 = “value2”, 214 | } 215 | 216 | 217 | 218 |

Returns:

219 |
    220 | 221 | table 222 | a table of all search parameters (a string:string map). 223 |
224 | 225 | 226 | 227 |

Usage:

228 |
    229 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f&a=g")
     230 | local result = search:decode()
    231 |
232 | 233 |
234 |
235 | 236 | search:decode () 237 |
238 |
239 | Decodes all search parameters and returns a Lua table of them.

240 | 241 |

An example return value: 242 | { 243 | key1 = { “first”, “second”, }, 244 | key2 = { “value” }, 245 | } 246 | 247 | 248 | 249 |

Returns:

250 |
    251 | 252 | table 253 | a table of all search parameters (a string:table [array] map). 254 |
255 | 256 | 257 | 258 |

Usage:

259 |
    260 |
    local search = require("resty.ada.search").parse("a=b&a=c&d=e")
     261 | local result = search:decode_all()
    262 |
263 | 264 |
265 |
266 |

Has Methods

267 | 268 |
269 |
270 | 271 | search:has (key) 272 |
273 |
274 | Checks whether the search has a key.

275 | 276 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-has 277 | 278 | 279 |

Parameters:

280 |
    281 |
  • key 282 | string 283 | search parameter name to check 284 |
  • 285 |
286 | 287 |

Returns:

288 |
    289 | 290 | boolean 291 | true if search has the key, otherwise false 292 |
293 | 294 |

Raises:

295 | error when key is not a string 296 | 297 | 298 |

Usage:

299 |
    300 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     301 | local result = search:has("a")
    302 |
303 | 304 |
305 |
306 | 307 | search:has_value (key, value) 308 |
309 |
310 | Checks whether the search has a key with a specific value.

311 | 312 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-has 313 | 314 | 315 |

Parameters:

316 |
    317 |
  • key 318 | string 319 | search parameter name to check 320 |
  • 321 |
  • value 322 | string 323 | search parameter value to check 324 |
  • 325 |
326 | 327 |

Returns:

328 |
    329 | 330 | boolean 331 | true if search has the key with the value, otherwise false 332 |
333 | 334 |

Raises:

335 | error when key or value is not a string 336 | 337 | 338 |

Usage:

339 |
    340 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     341 | local result = search:has_value("a", "b")
    342 |
343 | 344 |
345 |
346 |

Get Methods

347 | 348 |
349 |
350 | 351 | search:get (key) 352 |
353 |
354 | Get search parameter’s value.

355 | 356 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-get 357 | 358 | 359 |

Parameters:

360 |
    361 |
  • key 362 | string 363 | search parameter name 364 |
  • 365 |
366 | 367 |

Returns:

368 |
    369 | 370 | string or nil 371 | parameter value or nil 372 |
373 | 374 |

Raises:

375 | error when key is not a string 376 | 377 | 378 |

Usage:

379 |
    380 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     381 | local result = search:get("a")
    382 |
383 | 384 |
385 |
386 | 387 | search:get_all (key) 388 |
389 |
390 | Get all the search parameter’s values.

391 | 392 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-getall 393 | 394 | 395 |

Parameters:

396 |
    397 |
  • key 398 | string 399 | search parameter name 400 |
  • 401 |
402 | 403 |

Returns:

404 |
    405 | 406 | table 407 | array of all the values (or an empty array) 408 |
409 | 410 |

Raises:

411 | error when key is not a string 412 | 413 | 414 |

Usage:

415 |
    416 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     417 | local result = search:get_all("a")
    418 |
419 | 420 |
421 |
422 |

Set Methods

423 | 424 |
425 |
426 | 427 | search:reset (search) 428 |
429 |
430 | Sets (or resets) the search parameters. 431 | 432 | 433 |

Parameters:

434 |
    435 |
  • search 436 | string 437 | search to parse 438 |
  • 439 |
440 | 441 |

Returns:

442 |
    443 | 444 | search 445 | self 446 |
447 | 448 |

Raises:

449 | error when search is not a string 450 | 451 | 452 |

Usage:

453 |
    454 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     455 | print(search:reset("g=h"):tostring())
    456 |
457 | 458 |
459 |
460 | 461 | search:set (key, value) 462 |
463 |
464 | Set the search parameter’s value.

465 | 466 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-set 467 | 468 | 469 |

Parameters:

470 |
    471 |
  • key 472 | string 473 | search parameter name 474 |
  • 475 |
  • value 476 | string 477 | search parameter value 478 |
  • 479 |
480 | 481 |

Returns:

482 |
    483 | 484 | search 485 | self 486 |
487 | 488 |

Raises:

489 | error when key or value is not a string 490 | 491 | 492 |

Usage:

493 |
    494 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     495 | local result = search:set("a", "g"):tostring()
    496 |
497 | 498 |
499 |
500 | 501 | search:append (key, value) 502 |
503 |
504 | Append value to the the search parameter.

505 | 506 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-append 507 | 508 | 509 |

Parameters:

510 |
    511 |
  • key 512 | string 513 | search parameter name 514 |
  • 515 |
  • value 516 | string 517 | search parameter value 518 |
  • 519 |
520 | 521 |

Returns:

522 |
    523 | 524 | search 525 | self 526 |
527 | 528 |

Raises:

529 | error when key or value is not a string 530 | 531 | 532 |

Usage:

533 |
    534 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     535 | local result = search:append("a", "g"):tostring()
    536 |
537 | 538 |
539 |
540 |

Other Methods

541 | 542 |
543 |
544 | 545 | search:tostring () 546 |
547 |
548 | Return search parameters as a string.

549 | 550 |

See: https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior 551 | 552 | 553 | 554 |

Returns:

555 |
    556 | 557 | string 558 | string presentation of the search parameters 559 |
560 | 561 | 562 | 563 |

Usage:

564 |
    565 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     566 | local result = search:tostring()
    567 |
568 | 569 |
570 |
571 | 572 | search:sort () 573 |
574 |
575 | Sort search parameters.

576 | 577 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-sort 578 | 579 | 580 | 581 |

Returns:

582 |
    583 | 584 | search 585 | self 586 |
587 | 588 | 589 | 590 |

Usage:

591 |
    592 |
    local search = require("resty.ada.search").parse("e=f&c=d&a=b")
     593 | local result = search:sort():tostring()
    594 |
595 | 596 |
597 |
598 | 599 | search:size () 600 |
601 |
602 | Count search parameters. 603 | 604 | 605 | 606 |

Returns:

607 |
    608 | 609 | number 610 | search parameters count 611 |
612 | 613 | 614 | 615 |

Usage:

616 |
    617 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     618 | local result = search:size()
    619 |
620 | 621 |
622 |
623 |

Meta Methods

624 | 625 |
626 |
627 | 628 | search:__pairs () 629 |
630 |
631 | Iterate over each key and value in search parameters. 632 | 633 | 634 | 635 |

Returns:

636 |
    637 |
  1. 638 | function 639 | iterator function
  2. 640 |
  3. 641 | cdata 642 | state
  4. 643 |
644 | 645 | 646 | 647 |

Usage:

648 |
    649 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     650 | for key, value in pairs(search) do
     651 |   print(key, " = ", value)
     652 | end
    653 |
654 | 655 |
656 |
657 | 658 | search:__pairs () 659 |
660 |
661 | Iterate over each parameter in search parameters. 662 | 663 | 664 | 665 |

Returns:

666 |
    667 |
  1. 668 | function 669 | iterator function
  2. 670 |
  3. 671 | cdata 672 | state
  4. 673 |
674 | 675 | 676 | 677 |

Usage:

678 |
    679 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     680 | for i, param in ipairs(search) do
     681 |   print(i, ". ", param.key, " = ", param.value)
     682 | end
    683 |
684 | 685 |
686 |
687 | 688 | search:__tostring () 689 |
690 |
691 | Return search parameters as a string.

692 | 693 |

See: https://url.spec.whatwg.org/#urlsearchparams-stringification-behavior 694 | 695 | 696 | 697 |

Returns:

698 |
    699 | 700 | string 701 | string presentation of the search parameters 702 |
703 | 704 | 705 | 706 |

Usage:

707 |
    708 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     709 | local result = tostring(search)
    710 |
711 | 712 |
713 |
714 | 715 | search:__len () 716 |
717 |
718 | Count search parameters. 719 | 720 | 721 | 722 |

Returns:

723 |
    724 | 725 | number 726 | search parameters count 727 |
728 | 729 | 730 | 731 |

Usage:

732 |
    733 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     734 | local result = #search
    735 |
736 | 737 |
738 |
739 |

Destructor Method

740 | 741 |
742 |
743 | 744 | search:free () 745 |
746 |
747 | Explicitly destroys the Ada URL Search instance and frees the memory.

748 | 749 |

After calling this function, further calls will result runtime error. 750 | If this is not explicitly called, the memory is freed with garbage 751 | collector. 752 | 753 | 754 | 755 | 756 | 757 | 758 |

Usage:

759 |
    760 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     761 | search:free()
    762 |
763 | 764 |
765 |
766 |

Remove Methods

767 | 768 |
769 |
770 | 771 | search:remove (key) 772 |
773 |
774 | Remove search parameter.

775 | 776 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-delete 777 | 778 | 779 |

Parameters:

780 |
    781 |
  • key 782 | string 783 | search parameter name 784 |
  • 785 |
786 | 787 |

Returns:

788 |
    789 | 790 | search 791 | self 792 |
793 | 794 |

Raises:

795 | error when key is not a string 796 | 797 | 798 |

Usage:

799 |
    800 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     801 | local result = search:remove("a"):tostring()
    802 |
803 | 804 |
805 |
806 | 807 | search:remove_value (key, value) 808 |
809 |
810 | Remove search parameter’s value.

811 | 812 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-delete 813 | 814 | 815 |

Parameters:

816 |
    817 |
  • key 818 | string 819 | search parameter name 820 |
  • 821 |
  • value 822 | string 823 | search parameter’s value 824 |
  • 825 |
826 | 827 |

Returns:

828 |
    829 | 830 | search 831 | self 832 |
833 | 834 |

Raises:

835 | error when key or value is not a string 836 | 837 | 838 |

Usage:

839 |
    840 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     841 | local result = search:remove_value("a", "b"):tostring()
    842 |
843 | 844 |
845 |
846 |

Iterate Methods

847 | 848 |
849 |
850 | 851 | search:each () 852 |
853 |
854 | Iterate over search parameters. 855 | 856 | 857 | 858 |

Returns:

859 |
    860 |
  1. 861 | function 862 | iterator function
  2. 863 |
  3. 864 | cdata 865 | state
  4. 866 |
867 | 868 | 869 | 870 |

Usage:

871 |
    872 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     873 | for param in search:each() do
     874 |   print(param.key, " = ", param.value)
     875 | end
    876 |
877 | 878 |
879 |
880 | 881 | search:each_key () 882 |
883 |
884 | Iterate over each key in search parameters. 885 | 886 | 887 | 888 |

Returns:

889 |
    890 |
  1. 891 | function 892 | iterator function
  2. 893 |
  3. 894 | cdata 895 | state
  4. 896 |
897 | 898 | 899 | 900 |

Usage:

901 |
    902 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     903 | for key in search:each_key() do
     904 |   print("key: ", key)
     905 | end
    906 |
907 | 908 |
909 |
910 | 911 | search:each_value () 912 |
913 |
914 | Iterate over each value in search parameters. 915 | 916 | 917 | 918 |

Returns:

919 |
    920 |
  1. 921 | function 922 | iterator function
  2. 923 |
  3. 924 | cdata 925 | state
  4. 926 |
927 | 928 | 929 | 930 |

Usage:

931 |
    932 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     933 | for value in search:each_value() do
     934 |   print("value: ", value)
     935 | end
    936 |
937 | 938 |
939 |
940 | 941 | search:pairs () 942 |
943 |
944 | Iterate over each key and value in search parameters. 945 | 946 | 947 | 948 |

Returns:

949 |
    950 |
  1. 951 | function 952 | iterator function
  2. 953 |
  3. 954 | cdata 955 | state
  4. 956 |
957 | 958 | 959 | 960 |

Usage:

961 |
    962 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     963 | for key, value in search:pairs() do
     964 |   print(key, " = ", value)
     965 | end
    966 |
967 | 968 |
969 |
970 | 971 | search:ipairs () 972 |
973 |
974 | Iterate over each parameter in search parameters. 975 | 976 | 977 | 978 |

Returns:

979 |
    980 |
  1. 981 | function 982 | iterator function
  2. 983 |
  3. 984 | cdata 985 | state
  4. 986 |
987 | 988 | 989 | 990 |

Usage:

991 |
    992 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
     993 | for i, param in search:ipairs() do
     994 |   print(param.key, " = ", param.value)
     995 | end
    996 |
997 | 998 |
999 |
1000 | 1001 | 1002 |
1003 |
1004 |
1005 | generated by LDoc 1.5.0 1006 | Last updated 2024-09-03 15:49:45 1007 |
1008 |
1009 | 1010 | 1011 | -------------------------------------------------------------------------------- /docs/data/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/bungle/lua-resty-ada/ea95c91af82c4c2ed4e0e4e5a4253429ad346437/docs/data/icon.png -------------------------------------------------------------------------------- /docs/index.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 46 | 47 |
48 | 49 | 50 |

WHATWG-compliant and fast URL parser

51 |

lua-resty-ada implements a LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser

52 | 53 |

Modules

54 | 55 | 56 | 57 | 58 | 59 | 60 | 61 | 62 | 63 |
resty.adaProvides URL parsing and manipulation functionality.
resty.ada.searchProvides URL search parameter parsing and manipulation functionality.
64 |

Classes

65 | 66 | 67 | 68 | 69 | 70 | 71 | 72 | 73 | 74 |
urlAda URL
searchAda URL search parameters
75 | 76 |
77 |
78 |
79 | generated by LDoc 1.5.0 80 | Last updated 2024-09-03 15:49:45 81 |
82 |
83 | 84 | 85 | -------------------------------------------------------------------------------- /docs/ldoc_new.css: -------------------------------------------------------------------------------- 1 | body { 2 | color: #47555c; 3 | font-size: 16px; 4 | font-family: "Open Sans", sans-serif; 5 | margin: 0; 6 | background: #eff4ff; 7 | } 8 | 9 | a:link { color: #008fee; } 10 | a:visited { color: #008fee; } 11 | a:hover { color: #22a7ff; } 12 | 13 | h1 { font-size:26px; font-weight: normal; } 14 | h2 { font-size:22px; font-weight: normal; } 15 | h3 { font-size:18px; font-weight: normal; } 16 | h4 { font-size:16px; font-weight: bold; } 17 | 18 | hr { 19 | height: 1px; 20 | background: #c1cce4; 21 | border: 0px; 22 | margin: 15px 0; 23 | } 24 | 25 | code, tt { 26 | font-family: monospace; 27 | } 28 | span.parameter { 29 | font-family: monospace; 30 | font-weight: bold; 31 | color: rgb(99, 115, 131); 32 | } 33 | span.parameter:after { 34 | content:":"; 35 | } 36 | span.types:before { 37 | content:"("; 38 | } 39 | span.types:after { 40 | content:")"; 41 | } 42 | .type { 43 | font-weight: bold; font-style:italic 44 | } 45 | 46 | p.name { 47 | font-family: "Andale Mono", monospace; 48 | } 49 | 50 | #navigation { 51 | float: left; 52 | background-color: white; 53 | border-right: 1px solid #d3dbec; 54 | border-bottom: 1px solid #d3dbec; 55 | 56 | width: 14em; 57 | vertical-align: top; 58 | overflow: visible; 59 | } 60 | 61 | #navigation br { 62 | display: none; 63 | } 64 | 65 | #navigation h1 { 66 | background-color: white; 67 | border-bottom: 1px solid #d3dbec; 68 | padding: 15px; 69 | margin-top: 0px; 70 | margin-bottom: 0px; 71 | } 72 | 73 | #navigation h2 { 74 | font-size: 18px; 75 | background-color: white; 76 | border-bottom: 1px solid #d3dbec; 77 | padding-left: 15px; 78 | padding-right: 15px; 79 | padding-top: 10px; 80 | padding-bottom: 10px; 81 | margin-top: 30px; 82 | margin-bottom: 0px; 83 | } 84 | 85 | #content h1 { 86 | background-color: #2c3e67; 87 | color: white; 88 | padding: 15px; 89 | margin: 0px; 90 | } 91 | 92 | #content h2 { 93 | background-color: #6c7ea7; 94 | color: white; 95 | padding: 15px; 96 | padding-top: 15px; 97 | padding-bottom: 15px; 98 | margin-top: 0px; 99 | } 100 | 101 | #content h2 a { 102 | background-color: #6c7ea7; 103 | color: white; 104 | text-decoration: none; 105 | } 106 | 107 | #content h2 a:hover { 108 | text-decoration: underline; 109 | } 110 | 111 | #content h3 { 112 | font-style: italic; 113 | padding-top: 15px; 114 | padding-bottom: 4px; 115 | margin-right: 15px; 116 | margin-left: 15px; 117 | margin-bottom: 5px; 118 | border-bottom: solid 1px #bcd; 119 | } 120 | 121 | #content h4 { 122 | margin-right: 15px; 123 | margin-left: 15px; 124 | border-bottom: solid 1px #bcd; 125 | } 126 | 127 | #content pre { 128 | margin: 15px; 129 | } 130 | 131 | pre { 132 | background-color: rgb(50, 55, 68); 133 | color: white; 134 | border-radius: 3px; 135 | /* border: 1px solid #C0C0C0; /* silver */ 136 | padding: 15px; 137 | overflow: auto; 138 | font-family: "Andale Mono", monospace; 139 | } 140 | 141 | #content ul pre.example { 142 | margin-left: 0px; 143 | } 144 | 145 | table.index { 146 | /* border: 1px #00007f; */ 147 | } 148 | table.index td { text-align: left; vertical-align: top; } 149 | 150 | #navigation ul 151 | { 152 | font-size:1em; 153 | list-style-type: none; 154 | margin: 1px 1px 10px 1px; 155 | } 156 | 157 | #navigation li { 158 | text-indent: -1em; 159 | display: block; 160 | margin: 3px 0px 0px 22px; 161 | } 162 | 163 | #navigation li li a { 164 | margin: 0px 3px 0px -1em; 165 | } 166 | 167 | #content { 168 | margin-left: 14em; 169 | } 170 | 171 | #content p { 172 | padding-left: 15px; 173 | padding-right: 15px; 174 | } 175 | 176 | #content table { 177 | padding-left: 15px; 178 | padding-right: 15px; 179 | background-color: white; 180 | } 181 | 182 | #content p, #content table, #content ol, #content ul, #content dl { 183 | max-width: 900px; 184 | } 185 | 186 | #about { 187 | padding: 15px; 188 | padding-left: 16em; 189 | background-color: white; 190 | border-top: 1px solid #d3dbec; 191 | border-bottom: 1px solid #d3dbec; 192 | } 193 | 194 | table.module_list, table.function_list { 195 | border-width: 1px; 196 | border-style: solid; 197 | border-color: #cccccc; 198 | border-collapse: collapse; 199 | margin: 15px; 200 | } 201 | table.module_list td, table.function_list td { 202 | border-width: 1px; 203 | padding-left: 10px; 204 | padding-right: 10px; 205 | padding-top: 5px; 206 | padding-bottom: 5px; 207 | border: solid 1px rgb(193, 204, 228); 208 | } 209 | table.module_list td.name, table.function_list td.name { 210 | background-color: white; min-width: 200px; border-right-width: 0px; 211 | } 212 | table.module_list td.summary, table.function_list td.summary { 213 | background-color: white; width: 100%; border-left-width: 0px; 214 | } 215 | 216 | dl.function { 217 | margin-right: 15px; 218 | margin-left: 15px; 219 | border-bottom: solid 1px rgb(193, 204, 228); 220 | border-left: solid 1px rgb(193, 204, 228); 221 | border-right: solid 1px rgb(193, 204, 228); 222 | background-color: white; 223 | } 224 | 225 | dl.function dt { 226 | color: rgb(99, 123, 188); 227 | font-family: monospace; 228 | border-top: solid 1px rgb(193, 204, 228); 229 | padding: 15px; 230 | } 231 | 232 | dl.function dd { 233 | margin-left: 15px; 234 | margin-right: 15px; 235 | margin-top: 5px; 236 | margin-bottom: 15px; 237 | } 238 | 239 | #content dl.function dd h3 { 240 | margin-top: 0px; 241 | margin-left: 0px; 242 | padding-left: 0px; 243 | font-size: 16px; 244 | color: rgb(128, 128, 128); 245 | border-bottom: solid 1px #def; 246 | } 247 | 248 | #content dl.function dd ul, #content dl.function dd ol { 249 | padding: 0px; 250 | padding-left: 15px; 251 | list-style-type: none; 252 | } 253 | 254 | ul.nowrap { 255 | overflow:auto; 256 | white-space:nowrap; 257 | } 258 | 259 | .section-description { 260 | padding-left: 15px; 261 | padding-right: 15px; 262 | } 263 | 264 | /* stop sublists from having initial vertical space */ 265 | ul ul { margin-top: 0px; } 266 | ol ul { margin-top: 0px; } 267 | ol ol { margin-top: 0px; } 268 | ul ol { margin-top: 0px; } 269 | 270 | /* make the target distinct; helps when we're navigating to a function */ 271 | a:target + * { 272 | background-color: #FF9; 273 | } 274 | 275 | 276 | /* styles for prettification of source */ 277 | pre .comment { color: #bbccaa; } 278 | pre .constant { color: #a8660d; } 279 | pre .escape { color: #844631; } 280 | pre .keyword { color: #ffc090; font-weight: bold; } 281 | pre .library { color: #0e7c6b; } 282 | pre .marker { color: #512b1e; background: #fedc56; font-weight: bold; } 283 | pre .string { color: #8080ff; } 284 | pre .number { color: #f8660d; } 285 | pre .operator { color: #2239a8; font-weight: bold; } 286 | pre .preprocessor, pre .prepro { color: #a33243; } 287 | pre .global { color: #c040c0; } 288 | pre .user-keyword { color: #800080; } 289 | pre .prompt { color: #558817; } 290 | pre .url { color: #272fc2; text-decoration: underline; } 291 | -------------------------------------------------------------------------------- /docs/modules/resty.ada.search.html: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser 7 | 8 | 9 | 10 | 11 |
12 | 13 |
14 | 15 |
16 |
17 |
18 | 19 | 20 |
21 | 22 | 23 | 24 | 25 | 60 | 61 |
62 | 63 |

Module resty.ada.search

64 |

Provides URL search parameter parsing and manipulation functionality.

65 |

See: https://url.spec.whatwg.org/#interface-urlsearchparams

66 | 67 | 68 |

Constructors

69 | 70 | 71 | 72 | 73 | 74 |
parse (search)Parses search and returns an instance of Ada URL Search.
75 |

Has Functions

76 | 77 | 78 | 79 | 80 | 81 | 82 | 83 | 84 | 85 |
has (search, key)Checks whether the search has a key.
has_value (search, key, value)Checks whether the search has a key with a specific value.
86 |

Get Functions

87 | 88 | 89 | 90 | 91 | 92 | 93 | 94 | 95 | 96 |
get (search, key)Get search parameter’s value.
get_all (search, key)Get all the search parameter’s values.
97 |

Set Functions

98 | 99 | 100 | 101 | 102 | 103 | 104 | 105 | 106 | 107 |
set (search, key, value)Set the search parameter’s value.
append (search, key, value)Append value to the the search parameter.
108 |

Encode and Decode Functions

109 | 110 | 111 | 112 | 113 | 114 | 115 | 116 | 117 | 118 | 119 | 120 | 121 | 122 |
encode (params)Encodes search parameters and returns an query string.
decode (search)Decodes search parameters and returns a Lua table of them.
decode_all (search)Decodes all search parameters and returns a Lua table of them.
123 |

Remove Functions

124 | 125 | 126 | 127 | 128 | 129 | 130 | 131 | 132 | 133 |
remove (search, key)Remove search parameter.
remove_value (search, key, value)Remove search parameter’s value.
134 |

Iterate Functions

135 | 136 | 137 | 138 | 139 | 140 | 141 | 142 | 143 | 144 | 145 | 146 | 147 | 148 | 149 | 150 | 151 | 152 | 153 | 154 | 155 | 156 |
each (search)Iterate over search parameters.
each_key (search)Iterate over each key in search parameters.
each_value (search)Iterate over each value in search parameters.
pairs (search)Iterate over each key and value in search parameters.
ipairs (search)Iterate over each parameter in search parameters.
157 |

Other Functions

158 | 159 | 160 | 161 | 162 | 163 | 164 | 165 | 166 | 167 |
sort (search)Sort search parameters.
size (search)Count search parameters.
168 | 169 |
170 |
171 | 172 | 173 |

Constructors

174 | 175 |
176 |
177 | 178 | parse (search) 179 |
180 |
181 | Parses search and returns an instance of Ada URL Search.

182 | 183 |

See: https://url.spec.whatwg.org/#interface-urlsearchparams 184 | 185 | 186 |

Parameters:

187 |
    188 |
  • search 189 | string 190 | search to parse 191 |
  • 192 |
193 | 194 |

Returns:

195 |
    196 | 197 | search 198 | Ada URL Search instance 199 |
200 | 201 |

Raises:

202 | error when search is not a string 203 | 204 | 205 |

Usage:

206 |
    207 |
    local search = require("resty.ada.search").parse("a=b&c=d&e=f")
    208 |
209 | 210 |
211 |
212 |

Has Functions

213 | 214 |
215 |
216 | 217 | has (search, key) 218 |
219 |
220 | Checks whether the search has a key.

221 | 222 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-has 223 | 224 | 225 |

Parameters:

226 |
    227 |
  • search 228 | string 229 | search to parse 230 |
  • 231 |
  • key 232 | string 233 | search parameter name to check 234 |
  • 235 |
236 | 237 |

Returns:

238 |
    239 | 240 | boolean 241 | true if search has the key, otherwise false 242 |
243 | 244 |

Raises:

245 | error when search or key is not a string 246 | 247 | 248 |

Usage:

249 |
    250 |
    local search = require("resty.ada.search")
    251 | local result = search.has("a=b&c=d&e=f", "a")
    252 |
253 | 254 |
255 |
256 | 257 | has_value (search, key, value) 258 |
259 |
260 | Checks whether the search has a key with a specific value.

261 | 262 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-has 263 | 264 | 265 |

Parameters:

266 |
    267 |
  • search 268 | string 269 | search to parse 270 |
  • 271 |
  • key 272 | string 273 | search parameter name to check 274 |
  • 275 |
  • value 276 | string 277 | search parameter value to check 278 |
  • 279 |
280 | 281 |

Returns:

282 |
    283 | 284 | boolean 285 | true if search has the key with the value, otherwise false 286 |
287 | 288 |

Raises:

289 | error when search, key or value is not a string 290 | 291 | 292 |

Usage:

293 |
    294 |
    local search = require("resty.ada.search")
    295 | local result = search.has_value("a=b&c=d&e=f", "a", "b")
    296 |
297 | 298 |
299 |
300 |

Get Functions

301 | 302 |
303 |
304 | 305 | get (search, key) 306 |
307 |
308 | Get search parameter’s value.

309 | 310 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-get 311 | 312 | 313 |

Parameters:

314 |
    315 |
  • search 316 | string 317 | search to parse 318 |
  • 319 |
  • key 320 | string 321 | search parameter name 322 |
  • 323 |
324 | 325 |

Returns:

326 |
    327 | 328 | string or nil 329 | parameter value or nil 330 |
331 | 332 |

Raises:

333 | error when search or key is not a string 334 | 335 | 336 |

Usage:

337 |
    338 |
    local search = require("resty.ada.search")
    339 | local result = search.get("a=b&c=d&e=f", "a")
    340 |
341 | 342 |
343 |
344 | 345 | get_all (search, key) 346 |
347 |
348 | Get all the search parameter’s values.

349 | 350 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-getall 351 | 352 | 353 |

Parameters:

354 |
    355 |
  • search 356 | string 357 | search to parse 358 |
  • 359 |
  • key 360 | string 361 | search parameter name 362 |
  • 363 |
364 | 365 |

Returns:

366 |
    367 | 368 | table 369 | array of all the values (or an empty array) 370 |
371 | 372 |

Raises:

373 | error when search or key is not a string 374 | 375 | 376 |

Usage:

377 |
    378 |
    local search = require("resty.ada.search")
    379 | local result = search.get_all("a=b&c=d&e=f", "a")
    380 |
381 | 382 |
383 |
384 |

Set Functions

385 | 386 |
387 |
388 | 389 | set (search, key, value) 390 |
391 |
392 | Set the search parameter’s value.

393 | 394 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-set 395 | 396 | 397 |

Parameters:

398 |
    399 |
  • search 400 | string 401 | search to parse 402 |
  • 403 |
  • key 404 | string 405 | search parameter name 406 |
  • 407 |
  • value 408 | string 409 | search parameter value 410 |
  • 411 |
412 | 413 |

Returns:

414 |
    415 | 416 | string 417 | string presentation of the search parameters 418 |
419 | 420 |

Raises:

421 | error when search, key or value is not a string 422 | 423 | 424 |

Usage:

425 |
    426 |
    local search = require("resty.ada.search")
    427 | local result = search.set("a=b&c=d&e=f", "a", "g")
    428 |
429 | 430 |
431 |
432 | 433 | append (search, key, value) 434 |
435 |
436 | Append value to the the search parameter.

437 | 438 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-append 439 | 440 | 441 |

Parameters:

442 |
    443 |
  • search 444 | string 445 | search to parse 446 |
  • 447 |
  • key 448 | string 449 | search parameter name 450 |
  • 451 |
  • value 452 | string 453 | search parameter value 454 |
  • 455 |
456 | 457 |

Returns:

458 |
    459 | 460 | string 461 | string presentation of the search parameters 462 |
463 | 464 |

Raises:

465 | error when search, key or value is not a string 466 | 467 | 468 |

Usage:

469 |
    470 |
    local search = require("resty.ada.search")
    471 | local result = search.append("a=b&c=d&e=f", "a", "g")
    472 |
473 | 474 |
475 |
476 |

Encode and Decode Functions

477 | 478 |
479 |
480 | 481 | encode (params) 482 |
483 |
484 | Encodes search parameters and returns an query string.

485 | 486 |
    487 |
  • only string keys are allowed.
  • 488 |
  • only string, boolean and number values are allowed or an array of them
  • 489 |
  • false value is treated as missing (same as nil)
  • 490 |
  • true returns "" (empty string)
  • 491 |
  • negative and positive inf and NaN are not allowed as numbers in values
  • 492 |
493 | 494 | 495 |

When passing a table the keys will be sorted and with string the given order 496 | is preserved. 497 | 498 | 499 |

Parameters:

500 | 506 | 507 |

Returns:

508 |
    509 | 510 | string 511 | encoded query string 512 |
513 | 514 |

Raises:

515 | error when search or key is not a table or string, or when the rules above are not followed 516 | 517 | 518 |

Usage:

519 |
    520 |
    local search = require("resty.ada.search")
    521 | local result = search.encode({
    522 |   g = "h",
    523 |   a = { "f", "b", },
    524 |   c = "d",
    525 | })
    526 |
527 | 528 |
529 |
530 | 531 | decode (search) 532 |
533 |
534 | Decodes search parameters and returns a Lua table of them.

535 | 536 |

If same parameter appears multiple times, only the value of the 537 | first is returned.

538 | 539 |

Given the following query string: 540 | “a=b&c=d&e=f&a=g”

541 | 542 |

The following table is returned: 543 | { 544 | a = “b”, 545 | c = “d”, 546 | e = “f”, 547 | } 548 | 549 | 550 |

Parameters:

551 |
    552 |
  • search 553 | string 554 | search to parse 555 |
  • 556 |
557 | 558 |

Returns:

559 |
    560 | 561 | table 562 | a table of all search parameters (a string:string map). 563 |
564 | 565 |

Raises:

566 | error when search or key is not a string 567 | 568 | 569 |

Usage:

570 |
    571 |
    local search = require("resty.ada.search")
    572 | local result = search.decode("a=b&c=d&e=f&a=g")
    573 |
574 | 575 |
576 |
577 | 578 | decode_all (search) 579 |
580 |
581 | Decodes all search parameters and returns a Lua table of them.

582 | 583 |

Given the following query string: 584 | “a=b&a=c&d=e”"

585 | 586 |

The following table is returned: 587 | { 588 | a = { “b”, “c” }, 589 | d = { “e” }, 590 | } 591 | 592 | 593 |

Parameters:

594 |
    595 |
  • search 596 | string 597 | search to parse 598 |
  • 599 |
600 | 601 |

Returns:

602 |
    603 | 604 | table 605 | a table of all search parameters (a string:table [array] map). 606 |
607 | 608 |

Raises:

609 | error when search or key is not a string 610 | 611 | 612 |

Usage:

613 |
    614 |
    local search = require("resty.ada.search")
    615 | local result = search.decode_all("a=b&a=c&d=e")
    616 |
617 | 618 |
619 |
620 |

Remove Functions

621 | 622 |
623 |
624 | 625 | remove (search, key) 626 |
627 |
628 | Remove search parameter.

629 | 630 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-delete 631 | 632 | 633 |

Parameters:

634 |
    635 |
  • search 636 | string 637 | search to parse 638 |
  • 639 |
  • key 640 | string 641 | search parameter name 642 |
  • 643 |
644 | 645 |

Returns:

646 |
    647 | 648 | string 649 | string presentation of the search parameters 650 |
651 | 652 |

Raises:

653 | error when search or key is not a string 654 | 655 | 656 |

Usage:

657 |
    658 |
    local search = require("resty.ada.search")
    659 | local result = search.remove("a=b&c=d&e=f", "a")
    660 |
661 | 662 |
663 |
664 | 665 | remove_value (search, key, value) 666 |
667 |
668 | Remove search parameter’s value.

669 | 670 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-delete 671 | 672 | 673 |

Parameters:

674 |
    675 |
  • search 676 | string 677 | search to parse 678 |
  • 679 |
  • key 680 | string 681 | search parameter name 682 |
  • 683 |
  • value 684 | string 685 | search parameter’s value 686 |
  • 687 |
688 | 689 |

Returns:

690 |
    691 | 692 | string 693 | string presentation of the search parameters 694 |
695 | 696 |

Raises:

697 | error when search, key or value is not a string 698 | 699 | 700 |

Usage:

701 |
    702 |
    local search = require("resty.ada.search")
    703 | local result = search.remove_value("a=b&c=d&e=f", "a", "b")
    704 |
705 | 706 |
707 |
708 |

Iterate Functions

709 | 710 |
711 |
712 | 713 | each (search) 714 |
715 |
716 | Iterate over search parameters. 717 | 718 | 719 |

Parameters:

720 |
    721 |
  • search 722 | string 723 | search to parse 724 |
  • 725 |
726 | 727 |

Returns:

728 |
    729 |
  1. 730 | function 731 | iterator function
  2. 732 |
  3. 733 | cdata 734 | state
  4. 735 |
736 | 737 |

Raises:

738 | error when search is not a string 739 | 740 | 741 |

Usage:

742 |
    743 |
    local search = require("resty.ada.search")
    744 | for param in search.each("a=b&c=d&e=f") do
    745 |   print(param.key, " = ", param.value)
    746 | end
    747 |
748 | 749 |
750 |
751 | 752 | each_key (search) 753 |
754 |
755 | Iterate over each key in search parameters. 756 | 757 | 758 |

Parameters:

759 |
    760 |
  • search 761 | string 762 | search to parse 763 |
  • 764 |
765 | 766 |

Returns:

767 |
    768 |
  1. 769 | function 770 | iterator function
  2. 771 |
  3. 772 | cdata 773 | state
  4. 774 |
775 | 776 |

Raises:

777 | error when search is not a string 778 | 779 | 780 |

Usage:

781 |
    782 |
    local search = require("resty.ada.search")
    783 | for key in search.each_key("a=b&c=d&e=f") do
    784 |   print("key: ", key)
    785 | end
    786 |
787 | 788 |
789 |
790 | 791 | each_value (search) 792 |
793 |
794 | Iterate over each value in search parameters. 795 | 796 | 797 |

Parameters:

798 |
    799 |
  • search 800 | string 801 | search to parse 802 |
  • 803 |
804 | 805 |

Returns:

806 |
    807 |
  1. 808 | function 809 | iterator function
  2. 810 |
  3. 811 | cdata 812 | state
  4. 813 |
814 | 815 |

Raises:

816 | error when search is not a string 817 | 818 | 819 |

Usage:

820 |
    821 |
    local search = require("resty.ada.search")
    822 | for value in search.each_value("a=b&c=d&e=f") do
    823 |   print("value: ", value)
    824 | end
    825 |
826 | 827 |
828 |
829 | 830 | pairs (search) 831 |
832 |
833 | Iterate over each key and value in search parameters. 834 | 835 | 836 |

Parameters:

837 |
    838 |
  • search 839 | string 840 | search to parse 841 |
  • 842 |
843 | 844 |

Returns:

845 |
    846 |
  1. 847 | function 848 | iterator function
  2. 849 |
  3. 850 | cdata 851 | state
  4. 852 |
853 | 854 |

Raises:

855 | error when search is not a string 856 | 857 | 858 |

Usage:

859 |
    860 |
    local search = require("resty.ada.search")
    861 | for key, value in search.pairs("a=b&c=d&e=f") do
    862 |   print(key, " = ", value)
    863 | end
    864 |
865 | 866 |
867 |
868 | 869 | ipairs (search) 870 |
871 |
872 | Iterate over each parameter in search parameters. 873 | 874 | 875 |

Parameters:

876 |
    877 |
  • search 878 | string 879 | search to parse 880 |
  • 881 |
882 | 883 |

Returns:

884 |
    885 |
  1. 886 | function 887 | iterator function
  2. 888 |
  3. 889 | cdata 890 | state
  4. 891 |
892 | 893 |

Raises:

894 | error when search is not a string 895 | 896 | 897 |

Usage:

898 |
    899 |
    for i, param in search.ipairs("a=b&c=d&e=f") do
    900 |   print(i, ". ", param.key, " = ", param.value)
    901 | end
    902 |
903 | 904 |
905 |
906 |

Other Functions

907 | 908 |
909 |
910 | 911 | sort (search) 912 |
913 |
914 | Sort search parameters.

915 | 916 |

See: https://url.spec.whatwg.org/#dom-urlsearchparams-sort 917 | 918 | 919 |

Parameters:

920 |
    921 |
  • search 922 | string 923 | search to parse 924 |
  • 925 |
926 | 927 |

Returns:

928 |
    929 | 930 | string 931 | string presentation of the search parameters 932 |
933 | 934 |

Raises:

935 | error when search is not a string 936 | 937 | 938 |

Usage:

939 |
    940 |
    local search = require("resty.ada.search")
    941 | local result = search.sort("e=f&c=d&a=b")
    942 |
943 | 944 |
945 |
946 | 947 | size (search) 948 |
949 |
950 | Count search parameters. 951 | 952 | 953 |

Parameters:

954 |
    955 |
  • search 956 | string 957 | search to parse 958 |
  • 959 |
960 | 961 |

Returns:

962 |
    963 | 964 | number 965 | search parameters count 966 |
967 | 968 |

Raises:

969 | error when search is not a string 970 | 971 | 972 |

Usage:

973 |
    974 |
    local search = require("resty.ada.search")
    975 | local result = search.size("a=b&c=d&e=f")
    976 |
977 | 978 |
979 |
980 | 981 | 982 |
983 |
984 |
985 | generated by LDoc 1.5.0 986 | Last updated 2024-09-03 15:49:45 987 |
988 |
989 | 990 | 991 | -------------------------------------------------------------------------------- /lib/resty/ada/lib.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Provides Ada FFI definitions and library loading functionality. 3 | -- 4 | -- @local 5 | -- @module resty.ada.lib 6 | 7 | 8 | local ffi = require("ffi") 9 | 10 | 11 | local error = error 12 | local pcall = pcall 13 | local ipairs = ipairs 14 | local ffi_load = ffi.load 15 | 16 | 17 | ffi.cdef([[ 18 | typedef struct { 19 | const char* data; 20 | size_t length; 21 | } ada_string; 22 | 23 | typedef struct { 24 | const char* data; 25 | size_t length; 26 | } ada_owned_string; 27 | 28 | typedef struct { 29 | uint32_t protocol_end; 30 | uint32_t username_end; 31 | uint32_t host_start; 32 | uint32_t host_end; 33 | uint32_t port; 34 | uint32_t pathname_start; 35 | uint32_t search_start; 36 | uint32_t hash_start; 37 | } ada_url_components; 38 | 39 | typedef struct { 40 | ada_string key; 41 | ada_string value; 42 | } ada_string_pair; 43 | 44 | typedef void* ada_url; 45 | typedef void* ada_strings; 46 | typedef void* ada_url_search_params; 47 | typedef void* ada_url_search_params_keys_iter; 48 | typedef void* ada_url_search_params_values_iter; 49 | typedef void* ada_url_search_params_entries_iter; 50 | 51 | const ada_url_components* ada_get_components(ada_url result); 52 | 53 | ada_url ada_parse(const char* input, size_t length); 54 | ada_url ada_parse_with_base(const char* input, size_t input_length, const char* base, size_t base_length); 55 | ada_url_search_params ada_parse_search_params(const char* input, size_t length); 56 | 57 | bool ada_can_parse(const char* input, size_t length); 58 | bool ada_can_parse_with_base(const char* input, size_t input_length, const char* base, size_t base_length); 59 | 60 | void ada_free(ada_url result); 61 | void ada_free_owned_string(ada_owned_string owned); 62 | void ada_free_search_params(ada_url_search_params result); 63 | void ada_free_strings(ada_strings result); 64 | void ada_free_search_params_keys_iter(ada_url_search_params_keys_iter result); 65 | void ada_free_search_params_values_iter(ada_url_search_params_values_iter result); 66 | void ada_free_search_params_entries_iter(ada_url_search_params_entries_iter result); 67 | 68 | ada_url ada_copy(ada_url input); 69 | 70 | bool ada_is_valid(ada_url result); 71 | 72 | ada_owned_string ada_get_origin(ada_url result); 73 | ada_string ada_get_href(ada_url result); 74 | ada_string ada_get_username(ada_url result); 75 | ada_string ada_get_password(ada_url result); 76 | ada_string ada_get_port(ada_url result); 77 | ada_string ada_get_hash(ada_url result); 78 | ada_string ada_get_host(ada_url result); 79 | ada_string ada_get_hostname(ada_url result); 80 | ada_string ada_get_pathname(ada_url result); 81 | ada_string ada_get_search(ada_url result); 82 | ada_string ada_get_protocol(ada_url result); 83 | uint8_t ada_get_host_type(ada_url result); 84 | uint8_t ada_get_scheme_type(ada_url result); 85 | 86 | bool ada_set_href(ada_url result, const char* input, size_t length); 87 | bool ada_set_host(ada_url result, const char* input, size_t length); 88 | bool ada_set_hostname(ada_url result, const char* input, size_t length); 89 | bool ada_set_protocol(ada_url result, const char* input, size_t length); 90 | bool ada_set_username(ada_url result, const char* input, size_t length); 91 | bool ada_set_password(ada_url result, const char* input, size_t length); 92 | bool ada_set_port(ada_url result, const char* input, size_t length); 93 | bool ada_set_pathname(ada_url result, const char* input, size_t length); 94 | void ada_set_search(ada_url result, const char* input, size_t length); 95 | void ada_set_hash(ada_url result, const char* input, size_t length); 96 | 97 | void ada_clear_port(ada_url result); 98 | void ada_clear_hash(ada_url result); 99 | void ada_clear_search(ada_url result); 100 | 101 | bool ada_has_credentials(ada_url result); 102 | bool ada_has_empty_hostname(ada_url result); 103 | bool ada_has_hostname(ada_url result); 104 | bool ada_has_non_empty_username(ada_url result); 105 | bool ada_has_non_empty_password(ada_url result); 106 | bool ada_has_port(ada_url result); 107 | bool ada_has_password(ada_url result); 108 | bool ada_has_hash(ada_url result); 109 | bool ada_has_search(ada_url result); 110 | 111 | ada_owned_string ada_idna_to_unicode(const char* input, size_t length); 112 | ada_owned_string ada_idna_to_ascii(const char* input, size_t length); 113 | 114 | ada_owned_string ada_search_params_to_string(ada_url_search_params result); 115 | size_t ada_search_params_size(ada_url_search_params result); 116 | void ada_search_params_reset(ada_url_search_params result, const char* input, size_t length); 117 | void ada_search_params_sort(ada_url_search_params result); 118 | void ada_search_params_append(ada_url_search_params result, const char* key, size_t key_length, const char* value, size_t value_length); 119 | void ada_search_params_set(ada_url_search_params result, const char* key, size_t key_length, const char* value, size_t value_length); 120 | bool ada_search_params_has(ada_url_search_params result, const char* key, size_t key_length); 121 | bool ada_search_params_has_value(ada_url_search_params result, const char* key, size_t key_length, const char* value, size_t value_length); 122 | ada_string ada_search_params_get(ada_url_search_params result, const char* key, size_t key_length); 123 | ada_strings ada_search_params_get_all(ada_url_search_params result, const char* key, size_t key_length); 124 | 125 | ada_url_search_params_keys_iter ada_search_params_get_keys(ada_url_search_params result); 126 | ada_url_search_params_values_iter ada_search_params_get_values(ada_url_search_params result); 127 | ada_url_search_params_entries_iter ada_search_params_get_entries(ada_url_search_params result); 128 | 129 | bool ada_search_params_keys_iter_has_next(ada_url_search_params_keys_iter result); 130 | bool ada_search_params_values_iter_has_next(ada_url_search_params_values_iter result); 131 | bool ada_search_params_entries_iter_has_next(ada_url_search_params_entries_iter result); 132 | 133 | ada_string ada_search_params_keys_iter_next(ada_url_search_params_keys_iter result); 134 | ada_string ada_search_params_values_iter_next(ada_url_search_params_values_iter result); 135 | ada_string_pair ada_search_params_entries_iter_next(ada_url_search_params_entries_iter result); 136 | 137 | size_t ada_strings_size(ada_strings result); 138 | ada_string ada_strings_get(ada_strings result, size_t index); 139 | 140 | void ada_search_params_remove(ada_url_search_params result, const char* key, size_t key_length); 141 | void ada_search_params_remove_value(ada_url_search_params result, const char* key, size_t key_length, const char* value, size_t value_length); 142 | ]]) 143 | 144 | 145 | local function load_lib(name) 146 | local pok, lib = pcall(ffi_load, name) 147 | if pok then 148 | return lib 149 | end 150 | end 151 | 152 | 153 | local load_lib_from_cpath do 154 | local gmatch = string.gmatch 155 | local match = string.match 156 | local open = io.open 157 | local close = io.close 158 | local cpath = package.cpath 159 | function load_lib_from_cpath(name) 160 | for path, _ in gmatch(cpath, "[^;]+") do 161 | if path == "?.so" or path == "?.dylib" then 162 | path = "./" 163 | end 164 | local file_path = match(path, "(.*/)") 165 | file_path = file_path .. name 166 | local file = open(file_path) 167 | if file ~= nil then 168 | close(file) 169 | local lib = load_lib(file_path) 170 | return lib 171 | end 172 | end 173 | end 174 | end 175 | 176 | 177 | do 178 | local library_names = { 179 | "libada", 180 | "ada", 181 | } 182 | 183 | local library_versions = { 184 | "", 185 | ".3", 186 | ".2", 187 | } 188 | 189 | local library_extensions = ffi.os == "OSX" and { ".dylib", ".so", } 190 | or { ".so", ".dylib", } 191 | 192 | -- try to load ada library from package.cpath 193 | for _, library_name in ipairs(library_names) do 194 | for _, library_version in ipairs(library_versions) do 195 | for _, library_extension in ipairs(library_extensions) do 196 | local lib = load_lib_from_cpath(library_name .. library_version .. library_extension) 197 | if lib then 198 | return lib 199 | end 200 | end 201 | end 202 | end 203 | 204 | -- try to load ada library from normal system path 205 | for _, library_name in ipairs(library_names) do 206 | for _, library_version in ipairs(library_versions) do 207 | local lib = load_lib(library_name .. library_version) 208 | if lib then 209 | return lib 210 | end 211 | end 212 | end 213 | 214 | -- a final check before we give up 215 | local pok, lib = pcall(function() 216 | if ffi.C.ada_parse then 217 | return ffi.C 218 | end 219 | end) 220 | if pok then 221 | return lib 222 | end 223 | end 224 | 225 | 226 | error("unable to load ada library - please make sure that it can be found in package.cpath or system library path") 227 | -------------------------------------------------------------------------------- /lib/resty/ada/search.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Ada URL search parameters 3 | -- 4 | -- See: 5 | -- 6 | -- @classmod search 7 | 8 | 9 | local lib = require("resty.ada.lib") 10 | local utils = require("resty.ada.utils") 11 | 12 | 13 | local ada_string_to_lua = utils.ada_string_to_lua 14 | local ada_strings_to_lua = utils.ada_strings_to_lua 15 | local ada_owned_string_to_lua = utils.ada_owned_string_to_lua 16 | local number_to_string = utils.number_to_string 17 | 18 | 19 | local ffi_gc = require("ffi").gc 20 | 21 | 22 | local type = type 23 | local next = next 24 | local pairs = pairs 25 | local assert = assert 26 | local tonumber = tonumber 27 | local setmetatable = setmetatable 28 | 29 | 30 | local function each_iter(entries_iterator) 31 | if lib.ada_search_params_entries_iter_has_next(entries_iterator) then 32 | local pair = lib.ada_search_params_entries_iter_next(entries_iterator) 33 | return { 34 | key = ada_string_to_lua(pair.key), 35 | value = ada_string_to_lua(pair.value), 36 | } 37 | end 38 | 39 | ffi_gc(entries_iterator, nil) 40 | lib.ada_free_search_params_entries_iter(entries_iterator) 41 | end 42 | 43 | 44 | local function each_key_iter(keys_iterator) 45 | if lib.ada_search_params_keys_iter_has_next(keys_iterator) then 46 | local key = ada_string_to_lua(lib.ada_search_params_keys_iter_next(keys_iterator)) 47 | return key 48 | end 49 | 50 | ffi_gc(keys_iterator, nil) 51 | lib.ada_free_search_params_keys_iter(keys_iterator) 52 | end 53 | 54 | 55 | local function each_value_iter(values_iterator) 56 | if lib.ada_search_params_values_iter_has_next(values_iterator) then 57 | local value = ada_string_to_lua(lib.ada_search_params_values_iter_next(values_iterator)) 58 | return value 59 | end 60 | 61 | ffi_gc(values_iterator, nil) 62 | lib.ada_free_search_params_values_iter(values_iterator) 63 | end 64 | 65 | 66 | local function pairs_iter(entries_iterator) 67 | if lib.ada_search_params_entries_iter_has_next(entries_iterator) then 68 | local pair = lib.ada_search_params_entries_iter_next(entries_iterator) 69 | local key = ada_string_to_lua(pair.key) 70 | local value = ada_string_to_lua(pair.value) 71 | return key, value 72 | end 73 | 74 | ffi_gc(entries_iterator, nil) 75 | lib.ada_free_search_params_entries_iter(entries_iterator) 76 | end 77 | 78 | 79 | local function ipairs_iter(entries_iterator, invariant_state) 80 | if lib.ada_search_params_entries_iter_has_next(entries_iterator) then 81 | local pair = lib.ada_search_params_entries_iter_next(entries_iterator) 82 | local entry = { 83 | key = ada_string_to_lua(pair.key), 84 | value = ada_string_to_lua(pair.value), 85 | } 86 | return invariant_state + 1, entry 87 | end 88 | 89 | ffi_gc(entries_iterator, nil) 90 | lib.ada_free_search_params_entries_iter(entries_iterator) 91 | end 92 | 93 | 94 | local mt = {} 95 | 96 | 97 | mt.__index = mt 98 | 99 | 100 | --- 101 | -- Decode Methods 102 | -- @section decode-methods 103 | 104 | 105 | --- 106 | -- Decodes search parameters and returns a Lua table of them. 107 | -- 108 | -- If same parameter appears multiple times, only the value of the 109 | -- first is returned. 110 | -- 111 | -- An example return value: 112 | -- { 113 | -- key1 = "value", 114 | -- key2 = "value2", 115 | -- } 116 | -- 117 | -- @function decode 118 | -- @treturn table a table of all search parameters (a string:string map). 119 | -- 120 | -- @usage 121 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f&a=g") 122 | -- local result = search:decode() 123 | function mt:decode() 124 | if self:size() == 0 then 125 | return {} 126 | end 127 | local r = {} 128 | for k in self:each_key() do 129 | if not r[k] then 130 | r[k] = self:get(k) 131 | end 132 | end 133 | return r 134 | end 135 | 136 | 137 | --- 138 | -- Decodes all search parameters and returns a Lua table of them. 139 | -- 140 | -- An example return value: 141 | -- { 142 | -- key1 = { "first", "second", }, 143 | -- key2 = { "value" }, 144 | -- } 145 | -- 146 | -- @function decode 147 | -- @treturn table a table of all search parameters (a string:table [array] map). 148 | -- 149 | -- @usage 150 | -- local search = require("resty.ada.search").parse("a=b&a=c&d=e") 151 | -- local result = search:decode_all() 152 | function mt:decode_all() 153 | if self:size() == 0 then 154 | return {} 155 | end 156 | local r = {} 157 | for k in self:each_key() do 158 | if not r[k] then 159 | r[k] = self:get_all(k) 160 | end 161 | end 162 | return r 163 | end 164 | 165 | 166 | --- 167 | -- Has Methods 168 | -- @section has-methods 169 | 170 | 171 | --- 172 | -- Checks whether the search has a key. 173 | -- 174 | -- See: 175 | -- 176 | -- @function has 177 | -- @tparam string key search parameter name to check 178 | -- @treturn boolean `true` if search has the key, otherwise `false` 179 | -- @raise error when key is not a string 180 | -- 181 | -- @usage 182 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 183 | -- local result = search:has("a") 184 | function mt:has(key) 185 | assert(type(key) == "string", "invalid key") 186 | local r = lib.ada_search_params_has(self[1], key, #key) 187 | return r 188 | end 189 | 190 | 191 | --- 192 | -- Checks whether the search has a key with a specific value. 193 | -- 194 | -- See: 195 | -- 196 | -- @function has_value 197 | -- @tparam string key search parameter name to check 198 | -- @tparam string value search parameter value to check 199 | -- @treturn boolean `true` if search has the key with the value, otherwise `false` 200 | -- @raise error when key or value is not a string 201 | -- 202 | -- @usage 203 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 204 | -- local result = search:has_value("a", "b") 205 | function mt:has_value(key, value) 206 | assert(type(key) == "string", "invalid key") 207 | assert(type(value) == "string", "invalid value") 208 | local r = lib.ada_search_params_has_value(self[1], key, #key, value, #value) 209 | return r 210 | end 211 | 212 | 213 | --- 214 | -- Get Methods 215 | -- @section get-methods 216 | 217 | 218 | --- 219 | -- Get search parameter's value. 220 | -- 221 | -- See: 222 | -- 223 | -- @function get 224 | -- @tparam string key search parameter name 225 | -- @treturn string|nil parameter value or `nil` 226 | -- @raise error when key is not a string 227 | -- 228 | -- @usage 229 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 230 | -- local result = search:get("a") 231 | function mt:get(key) 232 | assert(type(key) == "string", "invalid key") 233 | local r = ada_string_to_lua(lib.ada_search_params_get(self[1], key, #key)) 234 | return r 235 | end 236 | 237 | 238 | --- 239 | -- Get all the search parameter's values. 240 | -- 241 | -- See: 242 | -- 243 | -- @function get_all 244 | -- @tparam string key search parameter name 245 | -- @treturn table array of all the values (or an empty array) 246 | -- @raise error when key is not a string 247 | -- 248 | -- @usage 249 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 250 | -- local result = search:get_all("a") 251 | function mt:get_all(key) 252 | assert(type(key) == "string", "invalid key") 253 | local r = ada_strings_to_lua(lib.ada_search_params_get_all(self[1], key, #key)) 254 | return r 255 | end 256 | 257 | 258 | --- 259 | -- Set Methods 260 | -- @section set-methods 261 | 262 | 263 | --- 264 | -- Sets (or resets) the search parameters. 265 | -- 266 | -- @function reset 267 | -- @tparam string search search to parse 268 | -- @treturn search self 269 | -- @raise error when search is not a string 270 | -- 271 | -- @usage 272 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 273 | -- print(search:reset("g=h"):tostring()) 274 | function mt:reset(search) 275 | assert(type(search) == "string", "invalid search") 276 | lib.ada_search_params_reset(self[1], search, #search) 277 | return self 278 | end 279 | 280 | 281 | --- 282 | -- Set the search parameter's value. 283 | -- 284 | -- See: 285 | -- 286 | -- @function set 287 | -- @tparam string key search parameter name 288 | -- @tparam string value search parameter value 289 | -- @treturn search self 290 | -- @raise error when key or value is not a string 291 | -- 292 | -- @usage 293 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 294 | -- local result = search:set("a", "g"):tostring() 295 | function mt:set(key, value) 296 | assert(type(key) == "string", "invalid key") 297 | assert(type(value) == "string", "invalid value") 298 | lib.ada_search_params_set(self[1], key, #key, value, #value) 299 | return self 300 | end 301 | 302 | 303 | --- 304 | -- Append value to the the search parameter. 305 | -- 306 | -- See: 307 | -- 308 | -- @function append 309 | -- @tparam string key search parameter name 310 | -- @tparam string value search parameter value 311 | -- @treturn search self 312 | -- @raise error when key or value is not a string 313 | -- 314 | -- @usage 315 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 316 | -- local result = search:append("a", "g"):tostring() 317 | function mt:append(key, value) 318 | assert(type(key) == "string", "invalid key") 319 | assert(type(value) == "string", "invalid value") 320 | lib.ada_search_params_append(self[1], key, #key, value, #value) 321 | return self 322 | end 323 | 324 | 325 | --- 326 | -- Remove Methods 327 | -- @section remove-methods 328 | 329 | 330 | --- 331 | -- Remove search parameter. 332 | -- 333 | -- See: 334 | -- 335 | -- @function remove 336 | -- @tparam string key search parameter name 337 | -- @treturn search self 338 | -- @raise error when key is not a string 339 | -- 340 | -- @usage 341 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 342 | -- local result = search:remove("a"):tostring() 343 | function mt:remove(key) 344 | assert(type(key) == "string", "invalid key") 345 | lib.ada_search_params_remove(self[1], key, #key) 346 | return self 347 | end 348 | 349 | 350 | --- 351 | -- Remove search parameter's value. 352 | -- 353 | -- See: 354 | -- 355 | -- @function remove_value 356 | -- @tparam string key search parameter name 357 | -- @tparam string value search parameter's value 358 | -- @treturn search self 359 | -- @raise error when key or value is not a string 360 | -- 361 | -- @usage 362 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 363 | -- local result = search:remove_value("a", "b"):tostring() 364 | function mt:remove_value(key, value) 365 | assert(type(key) == "string", "invalid key") 366 | assert(type(value) == "string", "invalid value") 367 | lib.ada_search_params_remove_value(self[1], key, #key, value, #value) 368 | return self 369 | end 370 | 371 | 372 | --- 373 | -- Iterate Methods 374 | -- @section iterate-methods 375 | 376 | 377 | --- 378 | -- Iterate over search parameters. 379 | -- 380 | -- @function each 381 | -- @treturn function iterator function 382 | -- @treturn cdata state 383 | -- 384 | -- @usage 385 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 386 | -- for param in search:each() do 387 | -- print(param.key, " = ", param.value) 388 | -- end 389 | function mt:each() 390 | local entries_iter = ffi_gc(lib.ada_search_params_get_entries(self[1]), lib.ada_free_search_params_entries_iter) 391 | return each_iter, entries_iter 392 | end 393 | 394 | 395 | --- 396 | -- Iterate over each key in search parameters. 397 | -- 398 | -- @function each_key 399 | -- @treturn function iterator function 400 | -- @treturn cdata state 401 | -- 402 | -- @usage 403 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 404 | -- for key in search:each_key() do 405 | -- print("key: ", key) 406 | -- end 407 | function mt:each_key() 408 | local keys_iter = ffi_gc(lib.ada_search_params_get_keys(self[1]), lib.ada_free_search_params_keys_iter) 409 | return each_key_iter, keys_iter 410 | end 411 | 412 | 413 | --- 414 | -- Iterate over each value in search parameters. 415 | -- 416 | -- @function each_value 417 | -- @treturn function iterator function 418 | -- @treturn cdata state 419 | -- 420 | -- @usage 421 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 422 | -- for value in search:each_value() do 423 | -- print("value: ", value) 424 | -- end 425 | function mt:each_value() 426 | local values_iter = ffi_gc(lib.ada_search_params_get_values(self[1]), lib.ada_free_search_params_values_iter) 427 | return each_value_iter, values_iter 428 | end 429 | 430 | 431 | --- 432 | -- Iterate over each key and value in search parameters. 433 | -- 434 | -- @function pairs 435 | -- @treturn function iterator function 436 | -- @treturn cdata state 437 | -- 438 | -- @usage 439 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 440 | -- for key, value in search:pairs() do 441 | -- print(key, " = ", value) 442 | -- end 443 | function mt:pairs() 444 | local entries_iter = ffi_gc(lib.ada_search_params_get_entries(self[1]), lib.ada_free_search_params_entries_iter) 445 | return pairs_iter, entries_iter 446 | end 447 | 448 | 449 | --- 450 | -- Iterate over each parameter in search parameters. 451 | -- 452 | -- @function ipairs 453 | -- @treturn function iterator function 454 | -- @treturn cdata state 455 | -- 456 | -- @usage 457 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 458 | -- for i, param in search:ipairs() do 459 | -- print(param.key, " = ", param.value) 460 | -- end 461 | function mt:ipairs() 462 | local entries_iter = ffi_gc(lib.ada_search_params_get_entries(self[1]), lib.ada_free_search_params_entries_iter) 463 | return ipairs_iter, entries_iter, 0 464 | end 465 | 466 | 467 | --- 468 | -- Other Methods 469 | -- @section other-methods 470 | 471 | 472 | --- 473 | -- Return search parameters as a string. 474 | -- 475 | -- See: 476 | -- 477 | -- @function tostring 478 | -- @treturn string string presentation of the search parameters 479 | -- 480 | -- @usage 481 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 482 | -- local result = search:tostring() 483 | function mt:tostring() 484 | local r = ada_owned_string_to_lua(lib.ada_search_params_to_string(self[1])) 485 | return r 486 | end 487 | 488 | 489 | --- 490 | -- Sort search parameters. 491 | -- 492 | -- See: 493 | -- 494 | -- @function sort 495 | -- @treturn search self 496 | -- 497 | -- @usage 498 | -- local search = require("resty.ada.search").parse("e=f&c=d&a=b") 499 | -- local result = search:sort():tostring() 500 | function mt:sort() 501 | lib.ada_search_params_sort(self[1]) 502 | return self 503 | end 504 | 505 | 506 | --- 507 | -- Count search parameters. 508 | -- 509 | -- @function size 510 | -- @treturn number search parameters count 511 | -- 512 | -- @usage 513 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 514 | -- local result = search:size() 515 | function mt:size() 516 | local r = tonumber(lib.ada_search_params_size(self[1]), 10) 517 | return r 518 | end 519 | 520 | 521 | --- 522 | -- Meta Methods 523 | -- @section meta-methods 524 | 525 | 526 | --- 527 | -- Iterate over each key and value in search parameters. 528 | -- 529 | -- @function __pairs 530 | -- @treturn function iterator function 531 | -- @treturn cdata state 532 | -- 533 | -- @usage 534 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 535 | -- for key, value in pairs(search) do 536 | -- print(key, " = ", value) 537 | -- end 538 | mt.__pairs = mt.pairs 539 | 540 | 541 | --- 542 | -- Iterate over each parameter in search parameters. 543 | -- 544 | -- @function __pairs 545 | -- @treturn function iterator function 546 | -- @treturn cdata state 547 | -- 548 | -- @usage 549 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 550 | -- for i, param in ipairs(search) do 551 | -- print(i, ". ", param.key, " = ", param.value) 552 | -- end 553 | mt.__ipairs = mt.ipairs 554 | 555 | 556 | --- 557 | -- Return search parameters as a string. 558 | -- 559 | -- See: 560 | -- 561 | -- @function __tostring 562 | -- @treturn string string presentation of the search parameters 563 | -- 564 | -- @usage 565 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 566 | -- local result = tostring(search) 567 | mt.__tostring = mt.tostring 568 | 569 | 570 | --- 571 | -- Count search parameters. 572 | -- 573 | -- @function __len 574 | -- @treturn number search parameters count 575 | -- 576 | -- @usage 577 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 578 | -- local result = #search 579 | mt.__len = mt.size 580 | 581 | 582 | --- 583 | -- Destructor Method 584 | -- @section destructor-method 585 | 586 | 587 | --- 588 | -- Explicitly destroys the Ada URL Search instance and frees the memory. 589 | -- 590 | -- After calling this function, further calls will result runtime error. 591 | -- If this is not explicitly called, the memory is freed with garbage 592 | -- collector. 593 | -- 594 | -- @function free 595 | -- 596 | -- @usage 597 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 598 | -- search:free() 599 | function mt:free() 600 | ffi_gc(self[1], nil) 601 | lib.ada_free_search_params(self[1]) 602 | self[1] = nil 603 | setmetatable(self, nil) 604 | end 605 | 606 | 607 | --- 608 | -- Provides URL search parameter parsing and manipulation functionality. 609 | -- 610 | -- See: 611 | -- 612 | -- @module resty.ada.search 613 | 614 | 615 | --- 616 | -- Constructors 617 | -- @section constructors 618 | 619 | 620 | --- 621 | -- Parses search and returns an instance of Ada URL Search. 622 | -- 623 | -- See: 624 | -- 625 | -- @function parse 626 | -- @tparam string search search to parse 627 | -- @treturn search Ada URL Search instance 628 | -- @raise error when search is not a string 629 | -- 630 | -- @usage 631 | -- local search = require("resty.ada.search").parse("a=b&c=d&e=f") 632 | local function parse(search) 633 | assert(type(search) == "string", "invalid search") 634 | return setmetatable({ 635 | ffi_gc(lib.ada_parse_search_params(search, #search), lib.ada_free_search_params) 636 | }, mt) 637 | end 638 | 639 | 640 | local S = parse("") -- just a dummy init value for this singleton 641 | 642 | 643 | --- 644 | -- Encode and Decode Functions 645 | -- @section encode-and-decode-function 646 | 647 | 648 | local function encode_value(v) 649 | if v == true or v == "" then 650 | return "" 651 | end 652 | 653 | if type(v) == "number" then 654 | v = number_to_string(v) 655 | end 656 | 657 | return v 658 | end 659 | 660 | 661 | --- 662 | -- Encodes search parameters and returns an query string. 663 | -- 664 | -- * only `string` keys are allowed. 665 | -- * only `string`, `boolean` and `number` values are allowed or an array of them 666 | -- * `false` value is treated as missing (same as `nil`) 667 | -- * `true` returns `""` (empty string) 668 | -- * negative and positive `inf` and `NaN` are not allowed as numbers in values 669 | -- 670 | -- When passing a table the keys will be sorted and with string the given order 671 | -- is preserved. 672 | -- 673 | -- @function encode 674 | -- @tparam table|string params search parameters to encode (either a `table` or `string`) 675 | -- @treturn string encoded query string 676 | -- @raise error when search or key is not a table or string, or when the rules above are not followed 677 | -- 678 | -- @usage 679 | -- local search = require("resty.ada.search") 680 | -- local result = search.encode({ 681 | -- g = "h", 682 | -- a = { "f", "b", }, 683 | -- c = "d", 684 | -- }) 685 | local function encode(params) 686 | if type(params) == "table" then 687 | if not next(params) then 688 | return "" 689 | end 690 | 691 | S:reset("") 692 | 693 | for k, v in pairs(params) do 694 | if v ~= false then 695 | if type(v) == "table" then 696 | for i = 1, #v do 697 | if v[i] then 698 | S:append(k, encode_value(v[i])) 699 | end 700 | end 701 | 702 | else 703 | S:append(k, encode_value(v)) 704 | end 705 | end 706 | end 707 | 708 | S:sort() 709 | 710 | else 711 | if params == "" or params == "?" then 712 | return "" 713 | end 714 | 715 | S:reset(params) 716 | end 717 | 718 | local r = S:tostring() 719 | return r 720 | end 721 | 722 | 723 | --- 724 | -- Decodes search parameters and returns a Lua table of them. 725 | -- 726 | -- If same parameter appears multiple times, only the value of the 727 | -- first is returned. 728 | -- 729 | -- Given the following query string: 730 | -- "a=b&c=d&e=f&a=g" 731 | -- 732 | -- The following table is returned: 733 | -- { 734 | -- a = "b", 735 | -- c = "d", 736 | -- e = "f", 737 | -- } 738 | -- 739 | -- @function decode 740 | -- @tparam string search search to parse 741 | -- @treturn table a table of all search parameters (a string:string map). 742 | -- @raise error when search or key is not a string 743 | -- 744 | -- @usage 745 | -- local search = require("resty.ada.search") 746 | -- local result = search.decode("a=b&c=d&e=f&a=g") 747 | local function decode(search) 748 | local r = S:reset(search):decode() 749 | return r 750 | end 751 | 752 | 753 | --- 754 | -- Decodes all search parameters and returns a Lua table of them. 755 | -- 756 | -- Given the following query string: 757 | -- "a=b&a=c&d=e"" 758 | -- 759 | -- The following table is returned: 760 | -- { 761 | -- a = { "b", "c" }, 762 | -- d = { "e" }, 763 | -- } 764 | -- 765 | -- @function decode_all 766 | -- @tparam string search search to parse 767 | -- @treturn table a table of all search parameters (a string:table [array] map). 768 | -- @raise error when search or key is not a string 769 | -- 770 | -- @usage 771 | -- local search = require("resty.ada.search") 772 | -- local result = search.decode_all("a=b&a=c&d=e") 773 | local function decode_all(search) 774 | local r = S:reset(search):decode_all() 775 | return r 776 | end 777 | 778 | 779 | --- 780 | -- Has Functions 781 | -- @section has-functions 782 | 783 | 784 | --- 785 | -- Checks whether the search has a key. 786 | -- 787 | -- See: 788 | -- 789 | -- @function has 790 | -- @tparam string search search to parse 791 | -- @tparam string key search parameter name to check 792 | -- @treturn boolean `true` if search has the key, otherwise `false` 793 | -- @raise error when search or key is not a string 794 | -- 795 | -- @usage 796 | -- local search = require("resty.ada.search") 797 | -- local result = search.has("a=b&c=d&e=f", "a") 798 | local function has(search, key) 799 | local r = S:reset(search):has(key) 800 | return r 801 | end 802 | 803 | 804 | --- 805 | -- Checks whether the search has a key with a specific value. 806 | -- 807 | -- See: 808 | -- 809 | -- @function has_value 810 | -- @tparam string search search to parse 811 | -- @tparam string key search parameter name to check 812 | -- @tparam string value search parameter value to check 813 | -- @treturn boolean `true` if search has the key with the value, otherwise `false` 814 | -- @raise error when search, key or value is not a string 815 | -- 816 | -- @usage 817 | -- local search = require("resty.ada.search") 818 | -- local result = search.has_value("a=b&c=d&e=f", "a", "b") 819 | local function has_value(search, key, value) 820 | local r = S:reset(search):has_value(key, value) 821 | return r 822 | end 823 | 824 | 825 | --- 826 | -- Get Functions 827 | -- @section get-functions 828 | 829 | 830 | --- 831 | -- Get search parameter's value. 832 | -- 833 | -- See: 834 | -- 835 | -- @function get 836 | -- @tparam string search search to parse 837 | -- @tparam string key search parameter name 838 | -- @treturn string|nil parameter value or `nil` 839 | -- @raise error when search or key is not a string 840 | -- 841 | -- @usage 842 | -- local search = require("resty.ada.search") 843 | -- local result = search.get("a=b&c=d&e=f", "a") 844 | local function get(search, key) 845 | local r = S:reset(search):get(key) 846 | return r 847 | end 848 | 849 | 850 | --- 851 | -- Get all the search parameter's values. 852 | -- 853 | -- See: 854 | -- 855 | -- @function get_all 856 | -- @tparam string search search to parse 857 | -- @tparam string key search parameter name 858 | -- @treturn table array of all the values (or an empty array) 859 | -- @raise error when search or key is not a string 860 | -- 861 | -- @usage 862 | -- local search = require("resty.ada.search") 863 | -- local result = search.get_all("a=b&c=d&e=f", "a") 864 | local function get_all(search, key) 865 | local r = S:reset(search):get_all(key) 866 | return r 867 | end 868 | 869 | 870 | --- 871 | -- Set Functions 872 | -- @section set-functions 873 | 874 | 875 | --- 876 | -- Set the search parameter's value. 877 | -- 878 | -- See: 879 | -- 880 | -- @function set 881 | -- @tparam string search search to parse 882 | -- @tparam string key search parameter name 883 | -- @tparam string value search parameter value 884 | -- @treturn string string presentation of the search parameters 885 | -- @raise error when search, key or value is not a string 886 | -- 887 | -- @usage 888 | -- local search = require("resty.ada.search") 889 | -- local result = search.set("a=b&c=d&e=f", "a", "g") 890 | local function set(search, key, value) 891 | local r = S:reset(search):set(key, value):tostring() 892 | return r 893 | end 894 | 895 | 896 | --- 897 | -- Append value to the the search parameter. 898 | -- 899 | -- See: 900 | -- 901 | -- @function append 902 | -- @tparam string search search to parse 903 | -- @tparam string key search parameter name 904 | -- @tparam string value search parameter value 905 | -- @treturn string string presentation of the search parameters 906 | -- @raise error when search, key or value is not a string 907 | -- 908 | -- @usage 909 | -- local search = require("resty.ada.search") 910 | -- local result = search.append("a=b&c=d&e=f", "a", "g") 911 | local function append(search, key, value) 912 | local r = S:reset(search):append(key, value):tostring() 913 | return r 914 | end 915 | 916 | 917 | --- 918 | -- Remove Functions 919 | -- @section remove-functions 920 | 921 | 922 | --- 923 | -- Remove search parameter. 924 | -- 925 | -- See: 926 | -- 927 | -- @function remove 928 | -- @tparam string search search to parse 929 | -- @tparam string key search parameter name 930 | -- @treturn string string presentation of the search parameters 931 | -- @raise error when search or key is not a string 932 | -- 933 | -- @usage 934 | -- local search = require("resty.ada.search") 935 | -- local result = search.remove("a=b&c=d&e=f", "a") 936 | local function remove(search, key) 937 | local r = S:reset(search):remove(key):tostring() 938 | return r 939 | end 940 | 941 | 942 | --- 943 | -- Remove search parameter's value. 944 | -- 945 | -- See: 946 | -- 947 | -- @function remove_value 948 | -- @tparam string search search to parse 949 | -- @tparam string key search parameter name 950 | -- @tparam string value search parameter's value 951 | -- @treturn string string presentation of the search parameters 952 | -- @raise error when search, key or value is not a string 953 | -- 954 | -- @usage 955 | -- local search = require("resty.ada.search") 956 | -- local result = search.remove_value("a=b&c=d&e=f", "a", "b") 957 | local function remove_value(search, key, value) 958 | local r = S:reset(search):remove_value(key, value):tostring() 959 | return r 960 | end 961 | 962 | 963 | --- 964 | -- Iterate Functions 965 | -- @section iterate-functions 966 | 967 | 968 | --- 969 | -- Iterate over search parameters. 970 | -- 971 | -- @function each 972 | -- @tparam string search search to parse 973 | -- @treturn function iterator function 974 | -- @treturn cdata state 975 | -- @raise error when search is not a string 976 | -- 977 | -- @usage 978 | -- local search = require("resty.ada.search") 979 | -- for param in search.each("a=b&c=d&e=f") do 980 | -- print(param.key, " = ", param.value) 981 | -- end 982 | local function each(search) 983 | local iterator, invariant_state = S:reset(search):each() 984 | return iterator, invariant_state 985 | end 986 | 987 | 988 | --- 989 | -- Iterate over each key in search parameters. 990 | -- 991 | -- @function each_key 992 | -- @tparam string search search to parse 993 | -- @treturn function iterator function 994 | -- @treturn cdata state 995 | -- @raise error when search is not a string 996 | -- 997 | -- @usage 998 | -- local search = require("resty.ada.search") 999 | -- for key in search.each_key("a=b&c=d&e=f") do 1000 | -- print("key: ", key) 1001 | -- end 1002 | local function each_key(search) 1003 | local iterator, invariant_state = S:reset(search):each_key() 1004 | return iterator, invariant_state 1005 | end 1006 | 1007 | 1008 | --- 1009 | -- Iterate over each value in search parameters. 1010 | -- 1011 | -- @function each_value 1012 | -- @tparam string search search to parse 1013 | -- @treturn function iterator function 1014 | -- @treturn cdata state 1015 | -- @raise error when search is not a string 1016 | -- 1017 | -- @usage 1018 | -- local search = require("resty.ada.search") 1019 | -- for value in search.each_value("a=b&c=d&e=f") do 1020 | -- print("value: ", value) 1021 | -- end 1022 | local function each_value(search) 1023 | local iterator, invariant_state = S:reset(search):each_value() 1024 | return iterator, invariant_state 1025 | end 1026 | 1027 | 1028 | --- 1029 | -- Iterate over each key and value in search parameters. 1030 | -- 1031 | -- @function pairs 1032 | -- @tparam string search search to parse 1033 | -- @treturn function iterator function 1034 | -- @treturn cdata state 1035 | -- @raise error when search is not a string 1036 | -- 1037 | -- @usage 1038 | -- local search = require("resty.ada.search") 1039 | -- for key, value in search.pairs("a=b&c=d&e=f") do 1040 | -- print(key, " = ", value) 1041 | -- end 1042 | local function search_pairs(search) 1043 | local iterator, invariant_state = S:reset(search):pairs() 1044 | return iterator, invariant_state 1045 | end 1046 | 1047 | 1048 | --- 1049 | -- Iterate over each parameter in search parameters. 1050 | -- 1051 | -- @function ipairs 1052 | -- @tparam string search search to parse 1053 | -- @treturn function iterator function 1054 | -- @treturn cdata state 1055 | -- @raise error when search is not a string 1056 | -- 1057 | -- @usage 1058 | -- for i, param in search.ipairs("a=b&c=d&e=f") do 1059 | -- print(i, ". ", param.key, " = ", param.value) 1060 | -- end 1061 | local function search_ipairs(search) 1062 | local iterator, invariant_state, initial_value = S:reset(search):ipairs() 1063 | return iterator, invariant_state, initial_value 1064 | end 1065 | 1066 | 1067 | --- 1068 | -- Other Functions 1069 | -- @section other-functions 1070 | 1071 | 1072 | --- 1073 | -- Sort search parameters. 1074 | -- 1075 | -- See: 1076 | -- 1077 | -- @function sort 1078 | -- @tparam string search search to parse 1079 | -- @treturn string string presentation of the search parameters 1080 | -- @raise error when search is not a string 1081 | -- 1082 | -- @usage 1083 | -- local search = require("resty.ada.search") 1084 | -- local result = search.sort("e=f&c=d&a=b") 1085 | local function sort(search) 1086 | local r = S:reset(search):sort():tostring() 1087 | return r 1088 | end 1089 | 1090 | 1091 | --- 1092 | -- Count search parameters. 1093 | -- 1094 | -- @function size 1095 | -- @tparam string search search to parse 1096 | -- @treturn number search parameters count 1097 | -- @raise error when search is not a string 1098 | -- 1099 | -- @usage 1100 | -- local search = require("resty.ada.search") 1101 | -- local result = search.size("a=b&c=d&e=f") 1102 | local function size(search) 1103 | local r = S:reset(search):size() 1104 | return r 1105 | end 1106 | 1107 | 1108 | return { 1109 | parse = parse, 1110 | encode = encode, 1111 | decode = decode, 1112 | decode_all = decode_all, 1113 | has = has, 1114 | has_value = has_value, 1115 | get = get, 1116 | get_all = get_all, 1117 | set = set, 1118 | append = append, 1119 | remove = remove, 1120 | remove_value = remove_value, 1121 | each = each, 1122 | each_key = each_key, 1123 | each_value = each_value, 1124 | pairs = search_pairs, 1125 | ipairs = search_ipairs, 1126 | sort = sort, 1127 | size = size, 1128 | } 1129 | -------------------------------------------------------------------------------- /lib/resty/ada/utils.lua: -------------------------------------------------------------------------------- 1 | --- 2 | -- Provides reusable utilities for `resty.ada`. 3 | -- 4 | -- @local 5 | -- @module resty.ada.utils 6 | 7 | 8 | local lib = require("resty.ada.lib") 9 | local new_tab = require("table.new") 10 | 11 | 12 | local ffi_str = require("ffi").string 13 | 14 | 15 | local tonumber = tonumber 16 | local tostring = tostring 17 | 18 | 19 | local POS_INF = 1/0 20 | local NEG_INF = -1/0 21 | 22 | 23 | local function ada_string_to_lua(result) 24 | if result.data == nil then -- nullptr equals to nil but is not falsy 25 | return nil 26 | end 27 | if result.length == 0 then 28 | return "" 29 | end 30 | local r = ffi_str(result.data, result.length) 31 | return r 32 | end 33 | 34 | 35 | local function ada_strings_to_lua(result) 36 | local size = tonumber(lib.ada_strings_size(result), 10) 37 | if size == 0 then 38 | lib.ada_free_strings(result) 39 | return {} 40 | end 41 | local r = new_tab(size, 0) 42 | for i = 1, size do 43 | r[i] = ada_string_to_lua(lib.ada_strings_get(result, i - 1)) 44 | end 45 | lib.ada_free_strings(result) 46 | return r 47 | end 48 | 49 | 50 | local function ada_owned_string_to_lua(ada_owned_string) 51 | local r = ada_string_to_lua(ada_owned_string) 52 | lib.ada_free_owned_string(ada_owned_string) 53 | return r 54 | end 55 | 56 | 57 | local function number_to_string(v) 58 | if v == POS_INF or v == NEG_INF or v ~= v then 59 | return nil 60 | end 61 | local r = tostring(v) 62 | return r 63 | end 64 | 65 | 66 | return { 67 | ada_string_to_lua = ada_string_to_lua, 68 | ada_strings_to_lua = ada_strings_to_lua, 69 | ada_owned_string_to_lua = ada_owned_string_to_lua, 70 | number_to_string = number_to_string, 71 | } 72 | -------------------------------------------------------------------------------- /lua-resty-ada-1.1.0-1.rockspec: -------------------------------------------------------------------------------- 1 | package = "lua-resty-ada" 2 | version = "1.1.0-1" 3 | source = { 4 | url = "git+https://github.com/bungle/lua-resty-ada.git", 5 | tag = "v1.1.0", 6 | } 7 | description = { 8 | summary = "LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser", 9 | detailed = "lua-resty-ada implements a LuaJIT FFI bindings to Ada — WHATWG-compliant and fast URL parser.", 10 | homepage = "https://github.com/bungle/lua-resty-ada", 11 | maintainer = "Aapo Talvensaari ", 12 | license = "BSD", 13 | } 14 | dependencies = { 15 | "lua >= 5.1", 16 | } 17 | build = { 18 | type = "builtin", 19 | modules = { 20 | ["resty.ada"] = "lib/resty/ada.lua", 21 | ["resty.ada.lib"] = "lib/resty/ada/lib.lua", 22 | ["resty.ada.search"] = "lib/resty/ada/search.lua", 23 | ["resty.ada.utils"] = "lib/resty/ada/utils.lua", 24 | }, 25 | } 26 | -------------------------------------------------------------------------------- /spec/01-ada-url_spec.lua: -------------------------------------------------------------------------------- 1 | local ada = require("resty.ada") 2 | 3 | 4 | local assert = assert 5 | local describe = describe 6 | local it = it 7 | local tostring = tostring 8 | 9 | 10 | local null = ngx.null 11 | local same = assert.same 12 | local equal = assert.equal 13 | local is_nil = assert.is_nil 14 | local is_true = assert.is_true 15 | local is_false = assert.is_false 16 | local is_table = assert.is_table 17 | local errors = assert.errors 18 | 19 | 20 | local function is_err(msg, ok, err) 21 | is_nil(ok) 22 | equal(msg, err) 23 | return ok, err 24 | end 25 | 26 | 27 | describe("Ada", function() 28 | describe("URL", function() 29 | describe(".parse", function() 30 | it("rejects invalid url", function() 31 | is_err("invalid url", ada.parse("")) 32 | end) 33 | it("accepts valid url", function() 34 | is_table(ada.parse("http://www.google.com/")) 35 | end) 36 | end) 37 | describe(".parse_with_base", function() 38 | it("rejects invalid url", function() 39 | is_err("invalid url or base", ada.parse_with_base("", "")) 40 | end) 41 | it("accepts valid url", function() 42 | is_table(ada.parse_with_base("/path?search#hash", "http://www.google.com")) 43 | end) 44 | end) 45 | describe(".idna_to_ascii", function() 46 | it("translates unicode domain to ascii", function() 47 | equal("www.xn--7eleven-506c.com", ada.idna_to_ascii("www.7‑Eleven.com")) 48 | end) 49 | end) 50 | describe(".idna_to_unicode", function() 51 | it("translates ascii encoded domain to unicode", function() 52 | equal("www.7‐eleven.com", ada.idna_to_unicode("www.xn--7eleven-506c.com")) 53 | end) 54 | end) 55 | describe(".can_parse", function() 56 | it("rejects invalid url", function() 57 | is_false(ada.can_parse(".com:443/Home/Privacy/Montréal")) 58 | is_false(ada.can_parse("")) 59 | end) 60 | it("accepts valid url", function() 61 | is_true(ada.can_parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 62 | end) 63 | end) 64 | describe(".can_parse_with_base", function() 65 | it("rejects invalid url", function() 66 | is_false(ada.can_parse_with_base("/path?search#hash", ".com:443/Home/Privacy/Montréal")) 67 | is_false(ada.can_parse_with_base("/path?search#hash", "")) 68 | is_false(ada.can_parse_with_base("", "http://")) 69 | end) 70 | it("accepts valid url", function() 71 | is_true(ada.can_parse_with_base("/path?search#hash", "http://www.google.com")) 72 | end) 73 | end) 74 | describe(".decode", function() 75 | it("works", function() 76 | same({ 77 | scheme_type = 2, 78 | protocol = "https:", 79 | username = "user", 80 | password = "pass", 81 | origin = "https://example.com:1234", 82 | host_type = 0, 83 | host = "example.com:1234", 84 | hostname = "example.com", 85 | port = 1234, 86 | pathname = "/foo/bar", 87 | search = "?baz", 88 | hash = "#quux", 89 | }, ada.decode("https://user:pass@example.com:1234/foo/bar?baz#quux")) 90 | is_err("invalid url", ada.decode("")) 91 | end) 92 | end) 93 | describe(".has_credentials", function() 94 | it("works", function() 95 | is_false(ada.has_credentials("https://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 96 | is_true(ada.has_credentials("https://foo:bar@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 97 | is_err("invalid url", ada.has_credentials("")) 98 | end) 99 | end) 100 | describe(".has_non_empty_username", function() 101 | it("works", function() 102 | is_false(ada.has_non_empty_username("https://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 103 | is_true(ada.has_non_empty_username("https://foo@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 104 | is_err("invalid url", ada.has_non_empty_username("")) 105 | end) 106 | end) 107 | describe(".has_password", function() 108 | it("works", function() 109 | is_false(ada.has_password("https://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 110 | is_false(ada.has_password("https://foo@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 111 | is_false(ada.has_password("https://foo:@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 112 | is_true(ada.has_password("https://foo:bar@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 113 | is_err("invalid url", ada.has_password("")) 114 | end) 115 | end) 116 | describe(".has_non_empty_password", function() 117 | it("works", function() 118 | is_false(ada.has_non_empty_password("https://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 119 | is_false(ada.has_non_empty_password("https://foo@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 120 | is_false(ada.has_non_empty_password("https://foo:@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 121 | is_true(ada.has_non_empty_password("https://foo:bar@www.7‑Eleven.com:443/Home/Privacy/Montréal")) 122 | is_err("invalid url", ada.has_non_empty_password("")) 123 | end) 124 | end) 125 | describe(".has_hostname", function() 126 | it("works", function() 127 | is_true(ada.has_hostname("https://www.7‑Eleven.com")) 128 | is_true(ada.has_hostname("file:///tmp/mock/path")) 129 | is_false(ada.has_hostname("non-spec:/.//p")) 130 | is_err("invalid url", ada.has_hostname("")) 131 | end) 132 | end) 133 | describe(".has_empty_hostname", function() 134 | it("works", function() 135 | is_false(ada.has_empty_hostname("https://www.7‑Eleven.com")) 136 | is_true(ada.has_empty_hostname("file:///tmp/mock/path")) 137 | is_false(ada.has_empty_hostname("non-spec:/.//p")) 138 | is_err("invalid url", ada.has_empty_hostname("")) 139 | end) 140 | end) 141 | describe(".has_port", function() 142 | it("works", function() 143 | is_false(ada.has_port("https://www.foo.com:443")) -- this is expected to return false on standard port 144 | is_false(ada.has_port("https://www.foo.com")) 145 | is_true(ada.has_port("https://www.foo.com:8888")) 146 | is_err("invalid url", ada.has_port("")) 147 | end) 148 | end) 149 | describe(".has_search", function() 150 | it("works", function() 151 | is_true(ada.has_search("https://www.foo.com?foo=bar")) 152 | is_false(ada.has_search("https://www.foo.com")) 153 | is_err("invalid url", ada.has_search("")) 154 | end) 155 | end) 156 | describe(".has_hash", function() 157 | it("works", function() 158 | is_true(ada.has_hash("https://www.foo.com?foo=bar#abc")) 159 | is_true(ada.has_hash("https://www.foo.com#abc")) 160 | is_false(ada.has_hash("https://www.foo.com")) 161 | is_err("invalid url", ada.has_hash("")) 162 | end) 163 | end) 164 | describe(".get_components", function() 165 | it("works", function() 166 | local c = ada.get_components("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 167 | equal(32, c.host_end) 168 | equal(9, c.host_start) 169 | equal(33, c.pathname_start) 170 | equal(7, c.protocol_end) 171 | equal(9, c.username_end) 172 | is_err("invalid url", ada.get_components("")) 173 | end) 174 | end) 175 | describe(".get_href", function() 176 | it("works", function() 177 | local u, err = ada.get_href("https://user:pass@host:1234/path?search#hash") 178 | equal("https://user:pass@host:1234/path?search#hash", u) 179 | is_nil(err) 180 | is_err("invalid url", ada.get_href("")) 181 | end) 182 | end) 183 | describe(".get_protocol", function() 184 | it("works", function() 185 | equal("https:", ada.get_protocol("https://www.foo.com")) 186 | is_nil(ada.get_protocol("www.foo.com?foo=bar")) 187 | is_err("invalid url", ada.get_protocol("")) 188 | end) 189 | end) 190 | describe(".get_scheme_type", function() 191 | it("works", function() 192 | equal(2, ada.get_scheme_type("https://www.foo.com")) 193 | equal(1, ada.get_scheme_type("grpc://www.foo.com")) 194 | equal(0, ada.get_scheme_type("http://www.foo.com")) 195 | is_nil(ada.get_scheme_type("www.foo.com")) 196 | is_err("invalid url", ada.get_scheme_type("")) 197 | end) 198 | end) 199 | describe(".get_origin", function() 200 | it("works", function() 201 | equal("https://www.foo.com", ada.get_origin("https://www.foo.com/foo/bar")) 202 | is_nil(ada.get_origin("www.foo.com/foo/bar")) 203 | is_err("invalid url", ada.get_origin("")) 204 | end) 205 | end) 206 | describe(".get_username", function() 207 | it("works", function() 208 | equal("foo", ada.get_username("https://foo:bar@www.foo.com?foo=bar")) 209 | equal("", ada.get_username("https://:bar@www.foo.com?foo=bar")) 210 | equal("", ada.get_username("https://:@www.foo.com?foo=bar")) 211 | equal("", ada.get_username("https://www.foo.com?foo=bar")) 212 | is_err("invalid url", ada.get_username("")) 213 | end) 214 | end) 215 | describe(".get_password", function() 216 | it("works", function() 217 | equal("bar", ada.get_password("https://foo:bar@www.foo.com?foo=bar")) 218 | equal("bar", ada.get_password("https://:bar@www.foo.com?foo=bar")) 219 | equal("", ada.get_password("https://:@www.foo.com?foo=bar")) 220 | equal("", ada.get_password("https://www.foo.com?foo=bar")) 221 | is_err("invalid url", ada.get_password("")) 222 | end) 223 | end) 224 | describe(".get_host", function() 225 | it("works", function() 226 | equal("www.foo.com", ada.get_host("https://foo:bar@www.foo.com?foo=bar")) 227 | equal("127.0.0.1", ada.get_host("https://127.0.0.1/foo/bar")) 228 | is_err("invalid url", ada.get_host("")) 229 | end) 230 | end) 231 | describe(".get_hostname", function() 232 | it("works", function() 233 | equal("www.foo.com", ada.get_hostname("https://foo:bar@www.foo.com?foo=bar")) 234 | equal("127.0.0.1", ada.get_hostname("https://127.0.0.1/foo/bar")) 235 | is_err("invalid url", ada.get_hostname("")) 236 | end) 237 | end) 238 | describe(".get_host_type", function() 239 | it("works", function() 240 | equal(0, ada.get_host_type("https://foo:bar@www.foo.com?foo=bar")) 241 | equal(1, ada.get_host_type("https://127.0.0.1/foo/bar")) 242 | is_err("invalid url", ada.get_host_type("")) 243 | end) 244 | end) 245 | describe(".get_port", function() 246 | it("works", function() 247 | equal("", ada.get_port("https://www.foo.com:443")) 248 | equal("", ada.get_port("https://www.foo.com")) 249 | equal(8888, ada.get_port("https://www.foo.com:8888")) 250 | is_err("invalid url", ada.get_port("")) 251 | end) 252 | end) 253 | describe(".get_pathname", function() 254 | it("works", function() 255 | equal("/", ada.get_pathname("https://foo:bar@www.foo.com?foo=bar")) 256 | equal("/foo/bar", ada.get_pathname("https://127.0.0.1/foo/bar")) 257 | is_err("invalid url", ada.get_pathname("")) 258 | end) 259 | end) 260 | describe(".get_search", function() 261 | it("works", function() 262 | equal("?foo=bar", ada.get_search("https://www.foo.com?foo=bar")) 263 | equal("?foo=bar&a=b&b=c", ada.get_search("https://www.foo.com?foo=bar&a=b&b=c")) 264 | equal("", ada.get_search("https://www.foo.com/")) 265 | equal("", ada.get_search("https://www.foo.com?")) 266 | is_err("invalid url", ada.get_search("")) 267 | end) 268 | end) 269 | describe(".get_hash", function() 270 | it("works", function() 271 | equal("#foo-bar", ada.get_hash("https://www.foo.com?foo=bar#foo-bar")) 272 | equal("", ada.get_hash("https://www.foo.com#")) 273 | equal("", ada.get_hash("https://www.foo.com")) 274 | is_err("invalid url", ada.get_hash("")) 275 | end) 276 | end) 277 | describe(".set_protocol", function() 278 | it("works", function() 279 | equal("http://www.xn--7eleven-506c.com/Home/Privacy/Montr%C3%A9al", ada.set_protocol("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "http")) 280 | equal("https:", ada.get_protocol("https://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 281 | equal("http:", ada.get_protocol("http://www.7‑Eleven.com:443/Home/Privacy/Montréal")) 282 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 283 | is_table(u:set_protocol("http")) 284 | equal("http:", u:get_protocol()) 285 | is_table(u:set_protocol("https")) 286 | equal("https:", u:get_protocol()) 287 | is_err("invalid url", ada.set_protocol("", "http")) 288 | is_err("unable to set protocol", ada.set_protocol("http://www.7‑Eleven.com:443/Home/Privacy/Montréal", "1234")) 289 | end) 290 | end) 291 | describe(".set_username", function() 292 | it("works", function() 293 | equal("https://user@www.xn--7eleven-506c.com/Home/Privacy/Montr%C3%A9al", ada.set_username("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "user")) 294 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 295 | is_table(u:set_username("user")) 296 | equal("user", u:get_username()) 297 | is_err("invalid url", ada.set_username("", "user")) 298 | is_err("unable to set username", ada.set_username("file:///doge", "user")) 299 | end) 300 | end) 301 | describe(".set_password", function() 302 | it("works", function() 303 | equal("https://:pass@www.xn--7eleven-506c.com/Home/Privacy/Montr%C3%A9al", ada.set_password("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "pass")) 304 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 305 | is_table(u:set_password("pass")) 306 | equal("pass", u:get_password()) 307 | is_err("invalid url", ada.set_password("", "pass")) 308 | is_err("unable to set password", ada.set_password("file:///doge", "pass")) 309 | end) 310 | end) 311 | describe(".set_host", function() 312 | it("works", function() 313 | equal("https://example.com/Home/Privacy/Montr%C3%A9al", ada.set_host("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "example.com")) 314 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 315 | is_table(u:set_host("example.com")) 316 | equal("example.com", u:get_host()) 317 | is_err("invalid url", ada.set_host("", "example.com")) 318 | is_err("unable to set host", ada.set_host("foo://example.com", "exa[mple.org")) 319 | end) 320 | end) 321 | describe(".set_hostname", function() 322 | it("works", function() 323 | equal("https://example.com/Home/Privacy/Montr%C3%A9al", ada.set_hostname("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "example.com")) 324 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 325 | is_table(u:set_hostname("example.com")) 326 | equal("example.com", u:get_host()) 327 | is_err("invalid url", ada.set_hostname("", "example.com")) 328 | is_err("unable to set hostname", ada.set_hostname("foo://example.com", "exa[mple.org")) 329 | end) 330 | end) 331 | describe(".set_port", function() 332 | it("works", function() 333 | errors(function() ada.set_port("https://host/", 0/0) end, "invalid port") 334 | errors(function() ada.set_port("https://host/", 1/0) end, "invalid port") 335 | errors(function() ada.set_port("https://host/", -1/0) end, "invalid port") 336 | equal("https://www.xn--7eleven-506c.com:1234/Home/Privacy/Montr%C3%A9al", ada.set_port("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", 1234)) 337 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy/Montréal") 338 | is_table(u:set_port(1234)) 339 | equal(1234, u:get_port()) 340 | is_err("invalid url", ada.set_port("", 1234)) 341 | is_err("unable to set port", ada.set_port("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "")) 342 | end) 343 | end) 344 | describe(".set_pathname", function() 345 | it("works", function() 346 | equal("https://www.xn--7eleven-506c.com/Doge", ada.set_pathname("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "/Doge")) 347 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy") 348 | equal("/Home/Privacy", u:get_pathname()) 349 | is_table(u:set_pathname("/foo/bar")) 350 | equal("/foo/bar", u:get_pathname()) 351 | is_err("invalid url", ada.set_pathname("", "/Doge")) 352 | is_err("unable to set pathname", ada.set_pathname("mailto:user@example.org", "/doge")) 353 | end) 354 | end) 355 | describe(".set_search", function() 356 | it("works", function() 357 | equal("https://www.xn--7eleven-506c.com/Home/Privacy/Montr%C3%A9al?foo=bar", ada.set_search("https://www.7‑Eleven.com:443/Home/Privacy/Montréal", "foo=bar")) 358 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy?foo=bar") 359 | equal("?foo=bar", u:get_search()) 360 | is_table(u:set_search("bar=baz")) 361 | equal("?bar=baz", u:get_search()) 362 | is_err("invalid url", ada.set_search("", "foo")) 363 | end) 364 | end) 365 | describe(".set_hash", function() 366 | it("works", function() 367 | equal("https://www.xn--7eleven-506c.com/Home/Privacy?foo=bar#bar", ada.set_hash("https://www.7‑Eleven.com:443/Home/Privacy?foo=bar#foo", "#bar")) 368 | local u = ada.parse("https://www.7‑Eleven.com:443/Home/Privacy?foo=bar#foo") 369 | equal("#foo", u:get_hash()) 370 | is_table(u:set_hash("#bar")) 371 | equal("#bar", u:get_hash()) 372 | is_err("invalid url", ada.set_hash("", "foo")) 373 | end) 374 | end) 375 | describe(".clear_port", function() 376 | it("works", function() 377 | equal("https://www.google.com/", ada.clear_port("https://www.google.com:8888/")) 378 | local u = ada.parse("https://www.google.com:8888/") 379 | equal("https://www.google.com/", tostring(u:clear_port())) 380 | is_err("invalid url", ada.clear_port("")) 381 | end) 382 | end) 383 | describe(".clear_search", function() 384 | it("works", function() 385 | equal("https://www.google.com:8888/", ada.clear_search("https://www.google.com:8888?foo=bar")) 386 | local u = ada.parse("https://www.google.com:8888?foo=bar") 387 | equal("https://www.google.com:8888/", tostring(u:clear_search())) 388 | is_err("invalid url", ada.clear_search("")) 389 | end) 390 | end) 391 | describe(".clear_hash", function() 392 | it("works", function() 393 | equal("https://www.google.com:8888/?foo=bar", ada.clear_hash("https://www.google.com:8888?foo=bar#bar")) 394 | local u = ada.parse("https://www.google.com:8888?foo=bar#bar") 395 | equal("https://www.google.com:8888/?foo=bar", tostring(u:clear_hash())) 396 | is_err("invalid url", ada.clear_hash("")) 397 | end) 398 | end) 399 | describe(".search_parse", function() 400 | it("works", function() 401 | local s = ada.search_parse("https://www.google.com?doge=z&jack=2") 402 | is_table(s) 403 | equal("doge=z&jack=2", s:tostring()) 404 | 405 | local url = ada.parse("https://www.google.com?doge=z&jack=2") 406 | s = url:search_parse() 407 | is_table(s) 408 | equal("doge=z&jack=2", s:tostring()) 409 | 410 | is_err("invalid url", ada.search_parse("")) 411 | end) 412 | end) 413 | describe(".search_encode", function() 414 | it("works", function() 415 | equal("doge=z&jack=2", ada.search_encode("https://www.google.com?doge=z&jack=2")) 416 | equal("", ada.search_encode("https://www.google.com")) 417 | equal("", ada.search_encode("https://www.google.com?")) 418 | equal("c=&e=&e=v&f=v", ada.search.encode({ a = false, b = nil, c = "", d = {}, e = { false, "", "v" }, f = "v" })) 419 | errors(function() ada.search.encode({ k = null }) end, "invalid value") 420 | errors(function() ada.search.encode({ k = 0/0 }) end, "invalid value") 421 | errors(function() ada.search.encode({ k = 1/0 }) end, "invalid value") 422 | errors(function() ada.search.encode({ k = -1/0 }) end, "invalid value") 423 | equal("", ada.search.encode({})) 424 | is_err("invalid url", ada.search_encode("")) 425 | end) 426 | end) 427 | describe(".search_decode", function() 428 | it("works", function() 429 | same({ doge = "z", jack = "2" }, ada.search_decode("https://www.google.com?doge=z&jack=2&doge=x")) 430 | same({}, ada.search_decode("https://www.google.com")) 431 | same({ doge = "z", jack = "2" }, ada.parse("https://www.google.com?doge=z&jack=2&doge=x"):search_decode()) 432 | same({}, ada.parse("https://www.google.com"):search_decode()) 433 | is_err("invalid url", ada.search_decode("")) 434 | end) 435 | end) 436 | describe(".search_decode_all", function() 437 | it("works", function() 438 | same({ doge = { "z", "x" }, jack = { "2" } }, ada.search_decode_all("https://www.google.com?doge=z&jack=2&doge=x")) 439 | same({}, ada.search_decode_all("https://www.google.com")) 440 | same({ doge = { "z", "x" }, jack = { "2" } }, ada.parse("https://www.google.com?doge=z&jack=2&doge=x"):search_decode_all()) 441 | same({}, ada.parse("https://www.google.com"):search_decode_all()) 442 | is_err("invalid url", ada.search_decode_all("")) 443 | end) 444 | end) 445 | describe(".search_has", function() 446 | it("works", function() 447 | is_true(ada.search_has("https://www.google.com?doge=z&jack=2", "jack")) 448 | local u = ada.parse("https://www.google.com?doge=z&jack=2") 449 | is_true(u:search_has("jack")) 450 | is_err("invalid url", ada.search_has("", "jack")) 451 | end) 452 | end) 453 | describe(".search_has_value", function() 454 | it("works", function() 455 | is_false(ada.search_has_value("https://www.google.com?doge=z&jack=2", "jack", "4")) 456 | is_true(ada.search_has_value("https://www.google.com?doge=z&jack=2", "jack", "2")) 457 | local u = ada.parse("https://www.google.com?doge=z&jack=2") 458 | is_false(u:search_has_value("jack", "4")) 459 | is_true(u:search_has_value("jack", "2")) 460 | is_err("invalid url", ada.search_has_value("", "jack", "2")) 461 | end) 462 | end) 463 | describe(".search_get", function() 464 | it("works", function() 465 | equal("2", ada.search_get("https://www.google.com?doge=z&jack=2", "jack")) 466 | is_nil(ada.search_get("https://www.google.com?doge=z&jack=2", "foo")) 467 | local u = ada.parse("https://www.google.com?doge=z&jack=2") 468 | equal("2", u:search_get("jack")) 469 | is_nil(u:search_get("foo")) 470 | is_err("invalid url", ada.search_get("", "jack")) 471 | end) 472 | end) 473 | describe(".search_get_all", function() 474 | it("works", function() 475 | same({}, ada.search_get_all("https://www.google.com?doge=z&jack=2&doge=s", "missing")) 476 | same({"z", "s"}, ada.search_get_all("https://www.google.com?doge=z&jack=2&doge=s", "doge")) 477 | local u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s") 478 | same({"z", "s"}, u:search_get_all("doge")) 479 | is_err("invalid url", ada.search_get_all("", "doge")) 480 | end) 481 | end) 482 | describe(".search_set", function() 483 | it("works", function() 484 | equal("https://www.google.com/?doge=z&jack=4", ada.search_set("https://www.google.com?doge=z&jack=2", "jack", "4")) 485 | local u = ada.parse("https://www.google.com?doge=z&jack=2") 486 | equal("https://www.google.com/?doge=z&jack=4", tostring(u:search_set("jack", "4"))) 487 | is_err("invalid url", ada.search_set("", "jack", "4")) 488 | end) 489 | end) 490 | describe(".search_append", function() 491 | it("works", function() 492 | equal("https://www.google.com/?doge=z&jack=2&doge=z", ada.search_append("https://www.google.com?doge=z&jack=2", "doge", "z")) 493 | local u = ada.parse("https://www.google.com?doge=z&jack=2") 494 | equal("https://www.google.com/?doge=z&jack=2&doge=z", tostring(u:search_append("doge", "z"))) 495 | is_err("invalid url", ada.search_append("", "jack", "4")) 496 | end) 497 | end) 498 | describe(".search_remove", function() 499 | it("works", function() 500 | equal("https://www.google.com/?jack=2&bug=&aa=3", ada.search_remove("https://www.google.com?doge=z&jack=2&doge=s&bug&aa=3", "doge")) 501 | local u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug&aa=3") 502 | equal("https://www.google.com/?jack=2&bug=&aa=3", tostring(u:search_remove("doge"))) 503 | is_err("invalid url", ada.search_remove("", "doge")) 504 | end) 505 | end) 506 | describe(".search_remove_value", function() 507 | it("works", function() 508 | equal("https://www.google.com/?doge=z&jack=2&doge=s&bug=&aa=3", ada.search_remove_value("https://www.google.com?doge=z&jack=2&doge=s&bug&aa=3", "doge", "t")) 509 | equal("https://www.google.com/?jack=2&doge=s&bug=&aa=3", ada.search_remove_value("https://www.google.com?doge=z&jack=2&doge=s&bug&aa=3", "doge", "z")) 510 | local u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug&aa=3") 511 | equal("https://www.google.com/?doge=z&jack=2&doge=s&bug=&aa=3", tostring(u:search_remove_value("doge", "t"))) 512 | u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug&aa=3") 513 | equal("https://www.google.com/?jack=2&doge=s&bug=&aa=3", tostring(u:search_remove_value("doge", "z"))) 514 | is_err("invalid url", ada.search_remove_value("", "doge", "z")) 515 | end) 516 | end) 517 | describe(".search_each", function() 518 | it("works", function() 519 | local args = { 520 | doge = true, 521 | jack = true, 522 | bug = true, 523 | aa = true, 524 | bb = true, 525 | } 526 | 527 | for t in ada.search_each("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 528 | is_true(args[t.key]) 529 | end 530 | 531 | for t in ada.search.each("?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 532 | is_true(args[t.key]) 533 | end 534 | 535 | local url = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") 536 | for t in url:search_each() do 537 | is_true(args[t.key]) 538 | end 539 | 540 | is_err("invalid url", ada.search_each("")) 541 | end) 542 | end) 543 | describe(".search_each_key", function() 544 | it("works", function() 545 | local args = { 546 | doge = true, 547 | jack = true, 548 | bug = true, 549 | aa = true, 550 | bb = true, 551 | } 552 | 553 | for k in ada.search_each_key("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 554 | is_true(args[k]) 555 | end 556 | 557 | for k in ada.search.each_key("?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 558 | is_true(args[k]) 559 | end 560 | 561 | local url = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") 562 | for k in url:search_each_key() do 563 | is_true(args[k]) 564 | end 565 | 566 | is_err("invalid url", ada.search_each_key("")) 567 | end) 568 | end) 569 | describe(".search_each_value", function() 570 | it("works", function() 571 | local args = { 572 | z = true, 573 | ["2"] = true, 574 | s = true, 575 | [""] = true, 576 | ["3"] = true, 577 | ["4"] = true, 578 | ["10"] = true, 579 | } 580 | 581 | for v in ada.search_each_value("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 582 | is_true(args[v]) 583 | end 584 | 585 | for v in ada.search.each_value("?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 586 | is_true(args[v]) 587 | end 588 | 589 | local url = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") 590 | for v in url:search_each_value() do 591 | is_true(args[v]) 592 | end 593 | 594 | is_err("invalid url", ada.search_each_value("")) 595 | end) 596 | end) 597 | describe(".search_pairs", function() 598 | it("works", function() 599 | local args = { 600 | doge = true, 601 | jack = true, 602 | bug = true, 603 | aa = true, 604 | bb = true, 605 | } 606 | 607 | for k in ada.search_pairs("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 608 | is_true(args[k]) 609 | end 610 | 611 | for k in ada.search.pairs("?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 612 | is_true(args[k]) 613 | end 614 | 615 | local url = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") 616 | for k in url:search_pairs() do 617 | is_true(args[k]) 618 | end 619 | 620 | is_err("invalid url", ada.search_pairs("")) 621 | end) 622 | end) 623 | describe(".search_ipairs", function() 624 | it("works", function() 625 | local args = { 626 | doge = true, 627 | jack = true, 628 | bug = true, 629 | aa = true, 630 | bb = true, 631 | } 632 | 633 | for _, v in ada.search_ipairs("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 634 | is_true(args[v.key]) 635 | end 636 | 637 | for _, v in ada.search.ipairs("?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") do 638 | is_true(args[v.key]) 639 | end 640 | 641 | local url = ada.parse("https://www.google.com?doge=z&jack=2&doge=s&bug=&aa=3&bb=4&bb=10") 642 | for _, v in url:search_ipairs() do 643 | is_true(args[v.key]) 644 | end 645 | 646 | is_err("invalid url", ada.search_ipairs("")) 647 | end) 648 | end) 649 | describe(".search_sort", function() 650 | it("works", function() 651 | equal("https://www.google.com/?doge=z&doge=s&jack=2", ada.search_sort("https://www.google.com?doge=z&jack=2&doge=s")) 652 | local u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s") 653 | equal("https://www.google.com/?doge=z&doge=s&jack=2", tostring(u:search_sort())) 654 | is_err("invalid url", ada.search_sort("")) 655 | end) 656 | end) 657 | describe(".search_size", function() 658 | it("works", function() 659 | equal(3, ada.search_size("https://www.google.com?doge=z&jack=2&doge=s")) 660 | local u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s") 661 | equal(3, u:search_size()) 662 | is_err("invalid url", ada.search_size("")) 663 | end) 664 | end) 665 | describe(":set_href", function() 666 | it("works", function() 667 | local u = ada.parse("https://localhost") 668 | is_table(u:set_href("https://www.google.com?doge=z&jack=2&doge=s")) 669 | equal("www.google.com", u:get_hostname()) 670 | is_err("unable to set href", u:set_href("")) 671 | equal("www.google.com", u:get_hostname()) 672 | end) 673 | end) 674 | describe(":free", function() 675 | it("works", function() 676 | local u = ada.parse("https://www.google.com?doge=z&jack=2&doge=s") 677 | equal("www.google.com", u:get_hostname()) 678 | u:free() 679 | errors(function() u:get_hostname() end, "attempt to call method 'get_hostname' (a nil value)") 680 | end) 681 | end) 682 | describe("__len metamethod", function() 683 | it("works", function() 684 | local u = ada.parse("https://www.google.com/?doge=z&jack=2&doge=s") 685 | equal(44, #u) 686 | end) 687 | end) 688 | end) 689 | describe("Search", function() 690 | describe(":free", function() 691 | it("works", function() 692 | local s = ada.search.parse("doge=z&jack=2&doge=s") 693 | equal("2", s:get("jack")) 694 | s:free() 695 | errors(function() s:get("jack") end, "attempt to call method 'get' (a nil value)") 696 | end) 697 | end) 698 | end) 699 | end) 700 | --------------------------------------------------------------------------------