@@ -81,6 +81,35 @@ pub const URL = struct {
8181 pub fn toWebApi (self : * const URL , allocator : Allocator ) ! WebApiURL {
8282 return WebApiURL .init (allocator , self .uri );
8383 }
84+
85+ /// Properly stitches two URL fragments together.
86+ ///
87+ /// For URLs with a path, it will replace the last entry with the src.
88+ /// For URLs without a path, it will add src as the path.
89+ pub fn stitch (allocator : std.mem.Allocator , src : []const u8 , base : []const u8 ) ! []const u8 {
90+ if (base .len == 0 ) {
91+ return src ;
92+ }
93+
94+ const protocol_end : usize = blk : {
95+ if (std .mem .indexOf (u8 , base , "://" )) | protocol_index | {
96+ break :blk protocol_index + 3 ;
97+ } else {
98+ break :blk 0 ;
99+ }
100+ };
101+
102+ if (std .mem .lastIndexOfScalar (u8 , base [protocol_end .. ], '/' )) | index | {
103+ const last_slash_pos = index + protocol_end ;
104+ if (last_slash_pos == base .len - 1 ) {
105+ return std .fmt .allocPrint (allocator , "{s}{s}" , .{ base , src });
106+ } else {
107+ return std .fmt .allocPrint (allocator , "{s}/{s}" , .{ base [0.. last_slash_pos ], src });
108+ }
109+ } else {
110+ return std .fmt .allocPrint (allocator , "{s}/{s}" , .{ base , src });
111+ }
112+ }
84113};
85114
86115test "Url resolve size" {
@@ -98,3 +127,45 @@ test "Url resolve size" {
98127 try std .testing .expectEqual (out_url .raw [25 ], '/' );
99128 try std .testing .expectEqualStrings (out_url .raw [26.. ], & url_string );
100129}
130+
131+ const testing = @import ("testing.zig" );
132+
133+ test "URL: Stitching Base & Src URLs (Basic)" {
134+ const allocator = testing .allocator ;
135+
136+ const base = "https://www.google.com/xyz/abc/123" ;
137+ const src = "something.js" ;
138+ const result = try URL .stitch (allocator , src , base );
139+ defer allocator .free (result );
140+ try testing .expectString ("https://www.google.com/xyz/abc/something.js" , result );
141+ }
142+
143+ test "URL: Stitching Base & Src URLs (Just Ending Slash)" {
144+ const allocator = testing .allocator ;
145+
146+ const base = "https://www.google.com/" ;
147+ const src = "something.js" ;
148+ const result = try URL .stitch (allocator , src , base );
149+ defer allocator .free (result );
150+ try testing .expectString ("https://www.google.com/something.js" , result );
151+ }
152+
153+ test "URL: Stitching Base & Src URLs (No Ending Slash)" {
154+ const allocator = testing .allocator ;
155+
156+ const base = "https://www.google.com" ;
157+ const src = "something.js" ;
158+ const result = try URL .stitch (allocator , src , base );
159+ defer allocator .free (result );
160+ try testing .expectString ("https://www.google.com/something.js" , result );
161+ }
162+
163+ test "URL: Stiching Base & Src URLs (Both Local)" {
164+ const allocator = testing .allocator ;
165+
166+ const base = "./abcdef/123.js" ;
167+ const src = "something.js" ;
168+ const result = try URL .stitch (allocator , src , base );
169+ defer allocator .free (result );
170+ try testing .expectString ("./abcdef/something.js" , result );
171+ }
0 commit comments