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 public import ada.c.wrapper;
29 
30 @("Simple URL parser user-friendly API")
31 @nogc nothrow unittest
32 {
33     import core.stdc.string : strlen; // @system
34     import ada.c.ada;
35 
36     // Test ada_parse
37     const(char)* input = "http://user:pass@example.com:8080/path/to/file.html?query=value#fragment";
38     auto url = ada_parse(input, strlen(input));
39     scope (exit)
40         ada_free(url);
41     assert(ada_is_valid(url));
42 }
43 
44 @("Search params parser key-value pair")
45 nothrow @nogc unittest
46 {
47     import core.stdc.string : strlen; // @system
48     import ada.c.ada;
49 
50     const(char)* input = "a=b&c=d&c=e&f=g";
51     auto url = ada_parse_search_params(input, strlen(input));
52 
53     assert(ada_search_params_size(url) == 4);
54 
55     const(char)* key = "key1";
56     const(char)* value = "value1";
57     const(char)* value2 = "value2";
58     ada_search_params_append(url, key, strlen(key), value, strlen(value));
59     assert(ada_search_params_size(url) == 5);
60 
61     ada_search_params_set(url, key, strlen(key), value2, strlen(value2));
62     assert(ada_search_params_size(url) == 5);
63 
64     assert(ada_search_params_has(url, key, strlen(key)));
65     assert(!ada_search_params_has_value(url, key, strlen(key), value, strlen(value)));
66     assert(ada_search_params_has_value(url, key, strlen(key), value2, strlen(value2)));
67 
68     auto result = ada_search_params_get_all(url, key, strlen(key));
69     assert(ada_strings_size(result) == 1);
70     ada_free_strings(result);
71 
72     auto keys = ada_search_params_get_keys(url);
73     auto values = ada_search_params_get_values(url);
74     auto entries = ada_search_params_get_entries(url);
75 
76     assert(ada_search_params_keys_iter_has_next(keys));
77     assert(ada_search_params_values_iter_has_next(values));
78     assert(ada_search_params_entries_iter_has_next(entries));
79 
80     assert(str_convert(ada_search_params_keys_iter_next(keys)) == "a");
81     assert(str_convert(ada_search_params_keys_iter_next(keys)) == "c");
82     assert(str_convert(ada_search_params_keys_iter_next(keys)) == "c");
83     assert(str_convert(ada_search_params_keys_iter_next(keys)) == "f");
84     assert(str_convert(ada_search_params_keys_iter_next(keys)) == "key1");
85     assert(!ada_search_params_keys_iter_has_next(keys));
86 
87     assert(str_convert(ada_search_params_values_iter_next(values)) == "b");
88     assert(str_convert(ada_search_params_values_iter_next(values)) == "d");
89     assert(str_convert(ada_search_params_values_iter_next(values)) == "e");
90     assert(str_convert(ada_search_params_values_iter_next(values)) == "g");
91     assert(str_convert(ada_search_params_values_iter_next(values)) == "value2");
92     assert(!ada_search_params_values_iter_has_next(values));
93 
94     auto pair = ada_search_params_entries_iter_next(entries);
95     assert(str_convert(pair.value) == "b");
96     assert(str_convert(pair.key) == "a");
97 
98     pair = ada_search_params_entries_iter_next(entries);
99     assert(str_convert(pair.value) == "d");
100     assert(str_convert(pair.key) == "c");
101 
102     while (ada_search_params_entries_iter_has_next(entries))
103     {
104         ada_search_params_entries_iter_next(entries);
105     }
106 
107     ada_search_params_remove(url, key, strlen(key));
108     ada_search_params_remove_value(url, key, strlen(key), value, strlen(value));
109 
110     auto str = ada_search_params_to_string(url);
111     assert(str_convert(str) == "a=b&c=d&c=e&f=g");
112 
113     ada_free_search_params_keys_iter(keys);
114     ada_free_search_params_values_iter(values);
115     ada_free_search_params_entries_iter(entries);
116     ada_free_owned_string(str);
117     ada_free_search_params(url);
118 }
119 
120 @("Wrapped API")
121 @safe nothrow @nogc unittest
122 {
123     // Test parsing a URL
124     auto url = AdaUrl(ParseOptions("https://example.com/path?query=value#hash"));
125     assert(url.isValid());
126 
127     assert(url.getProtocol() == "https:");
128     assert(url.getHost() == "example.com");
129     assert(url.getPathname() == "/path");
130     assert(url.getSearch().toString().str_convert() == "query=value");
131     assert(url.getHash() == "#hash");
132 
133     // Test setting URL components
134     url.setProtocol("http");
135     assert(url.getProtocol() == "http:");
136     url.setHost("example.org");
137     assert(url.getHost() == "example.org");
138     url.setPathname("/new-path");
139     assert(url.getPathname() == "/new-path");
140     url.setSearch("?new=value");
141     assert(url.getSearch().toString().str_convert() == "new=value");
142     url.setHash("#new-hash");
143     assert(url.getHash() == "#new-hash");
144 
145     // Test clearing URL components
146     url.clearPort();
147     assert(!url.hasPort());
148     url.clearHash();
149     assert(!url.hasHash());
150     url.clearSearch();
151     assert(!url.hasSearch());
152 
153     // Test parsing with base URL
154     auto baseUrl = AdaUrl(ParseOptions("https://example.com/base"));
155     auto relativeUrl = AdaUrl(ParseOptions("/relative/path?query=value#hash", baseUrl.getHref()));
156     assert(relativeUrl.getProtocol() == "https:");
157     assert(relativeUrl.getHost() == "example.com");
158     assert(relativeUrl.getPathname() == "/relative/path");
159     assert(relativeUrl.getSearch().toString().str_convert() == "query=value");
160     assert(relativeUrl.getHash() == "#hash");
161 }