1 /* 2 * Copyright 2025 Matheus C. França 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 module ada.url; 17 18 version (D_BetterC) // no DRT 19 { 20 /// if betterC and LDC, disable moduleinfo and typeinfo 21 version (LDC) 22 { 23 pragma(LDC_no_moduleinfo); 24 pragma(LDC_no_typeinfo); 25 } 26 } 27 28 /// Ada-URL RAII. 29 public import ada.c.wrapper; 30 31 version (unittest) 32 { 33 @("Simple URL parser user-friendly API") 34 @nogc nothrow unittest 35 { 36 import core.stdc.string : strlen; // @system 37 import ada.c.ada; 38 39 // Test ada_parse 40 const(char)* input = "http://user:pass@example.com:8080/path/to/file.html?query=value#fragment"; 41 auto url = ada_parse(input, strlen(input)); 42 scope (exit) 43 ada_free(url); 44 assert(ada_is_valid(url)); 45 } 46 47 @("Search params parser key-value pair") 48 nothrow @nogc unittest 49 { 50 import core.stdc.string : strlen; // @system 51 import ada.c.ada; 52 53 const(char)* input = "a=b&c=d&c=e&f=g"; 54 auto url = ada_parse_search_params(input, strlen(input)); 55 56 assert(ada_search_params_size(url) == 4); 57 58 const(char)* key = "key1"; 59 const(char)* value = "value1"; 60 const(char)* value2 = "value2"; 61 ada_search_params_append(url, key, strlen(key), value, strlen(value)); 62 assert(ada_search_params_size(url) == 5); 63 64 ada_search_params_set(url, key, strlen(key), value2, strlen(value2)); 65 assert(ada_search_params_size(url) == 5); 66 67 assert(ada_search_params_has(url, key, strlen(key))); 68 assert(!ada_search_params_has_value(url, key, strlen(key), value, strlen(value))); 69 assert(ada_search_params_has_value(url, key, strlen(key), value2, strlen(value2))); 70 71 auto result = ada_search_params_get_all(url, key, strlen(key)); 72 assert(ada_strings_size(result) == 1); 73 ada_free_strings(result); 74 75 auto keys = ada_search_params_get_keys(url); 76 auto values = ada_search_params_get_values(url); 77 auto entries = ada_search_params_get_entries(url); 78 79 assert(ada_search_params_keys_iter_has_next(keys)); 80 assert(ada_search_params_values_iter_has_next(values)); 81 assert(ada_search_params_entries_iter_has_next(entries)); 82 83 assert(str_convert(ada_search_params_keys_iter_next(keys)) == "a"); 84 assert(str_convert(ada_search_params_keys_iter_next(keys)) == "c"); 85 assert(str_convert(ada_search_params_keys_iter_next(keys)) == "c"); 86 assert(str_convert(ada_search_params_keys_iter_next(keys)) == "f"); 87 assert(str_convert(ada_search_params_keys_iter_next(keys)) == "key1"); 88 assert(!ada_search_params_keys_iter_has_next(keys)); 89 90 assert(str_convert(ada_search_params_values_iter_next(values)) == "b"); 91 assert(str_convert(ada_search_params_values_iter_next(values)) == "d"); 92 assert(str_convert(ada_search_params_values_iter_next(values)) == "e"); 93 assert(str_convert(ada_search_params_values_iter_next(values)) == "g"); 94 assert(str_convert(ada_search_params_values_iter_next(values)) == "value2"); 95 assert(!ada_search_params_values_iter_has_next(values)); 96 97 auto pair = ada_search_params_entries_iter_next(entries); 98 assert(str_convert(pair.value) == "b"); 99 assert(str_convert(pair.key) == "a"); 100 101 pair = ada_search_params_entries_iter_next(entries); 102 assert(str_convert(pair.value) == "d"); 103 assert(str_convert(pair.key) == "c"); 104 105 while (ada_search_params_entries_iter_has_next(entries)) 106 { 107 ada_search_params_entries_iter_next(entries); 108 } 109 110 ada_search_params_remove(url, key, strlen(key)); 111 ada_search_params_remove_value(url, key, strlen(key), value, strlen(value)); 112 113 auto str = ada_search_params_to_string(url); 114 assert(str_convert(str) == "a=b&c=d&c=e&f=g"); 115 116 ada_free_search_params_keys_iter(keys); 117 ada_free_search_params_values_iter(values); 118 ada_free_search_params_entries_iter(entries); 119 ada_free_owned_string(str); 120 ada_free_search_params(url); 121 } 122 123 @("Wrapped API") 124 @safe nothrow @nogc unittest 125 { 126 // Test parsing a URL 127 auto url = AdaUrl(ParseOptions("https://example.com/path?query=value#hash")); 128 assert(url.isValid()); 129 130 assert(url.getProtocol() == "https:"); 131 assert(url.getHost() == "example.com"); 132 assert(url.getPathname() == "/path"); 133 assert(url.getSearch().toString().str_convert() == "query=value"); 134 assert(url.getHash() == "#hash"); 135 136 // Test setting URL components 137 url.setProtocol("http"); 138 assert(url.getProtocol() == "http:"); 139 url.setHost("example.org"); 140 assert(url.getHost() == "example.org"); 141 url.setPathname("/new-path"); 142 assert(url.getPathname() == "/new-path"); 143 url.setSearch("?new=value"); 144 assert(url.getSearch().toString().str_convert() == "new=value"); 145 url.setHash("#new-hash"); 146 assert(url.getHash() == "#new-hash"); 147 148 // Test clearing URL components 149 url.clearPort(); 150 assert(!url.hasPort()); 151 url.clearHash(); 152 assert(!url.hasHash()); 153 url.clearSearch(); 154 assert(!url.hasSearch()); 155 156 // Test parsing with base URL 157 auto baseUrl = AdaUrl(ParseOptions("https://example.com/base")); 158 auto relativeUrl = AdaUrl(ParseOptions("/relative/path?query=value#hash", baseUrl.getHref())); 159 assert(relativeUrl.getProtocol() == "https:"); 160 assert(relativeUrl.getHost() == "example.com"); 161 assert(relativeUrl.getPathname() == "/relative/path"); 162 assert(relativeUrl.getSearch().toString().str_convert() == "query=value"); 163 assert(relativeUrl.getHash() == "#hash"); 164 } 165 }