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 }