Skip to content

Commit f83ac2b

Browse files
jiacai2050Copilot
andauthored
refactor: no allocator inside Easy (#30)
Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
1 parent 85af8d9 commit f83ac2b

10 files changed

Lines changed: 287 additions & 172 deletions

File tree

README.org

Lines changed: 8 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
#+TITLE: zig-curl
22
#+DATE: 2023-09-16T23:16:15+0800
3-
#+LASTMOD: 2025-03-09T10:42:45+0800
3+
#+LASTMOD: 2025-05-18T21:07:57+0800
44
#+OPTIONS: toc:nil num:nil
55
#+STARTUP: content
66

@@ -31,26 +31,28 @@ pub fn main() !void {
3131
defer if (gpa.deinit() != .ok) @panic("leak");
3232
const allocator = gpa.allocator();
3333

34-
const easy = try curl.Easy.init(allocator, .{});
34+
const easy = try curl.Easy.init(.{});
3535
defer easy.deinit();
3636

37-
const resp = try easy.get("http://httpbin.org/anything");
37+
const resp = try easy.fetchAlloc("http://httpbin.org/anything", allocator, .{});
3838
defer resp.deinit();
3939

4040
std.debug.print("Status code: {d}\nBody: {s}\n", .{
4141
resp.status_code,
42-
resp.body.items,
42+
resp.body.?.slice(),
4343
});
4444
}
4545
#+end_src
4646
See [[file:examples/basic.zig]], [[file:examples/advanced.zig]] for more usage.
4747

4848
* Installation
4949
#+begin_src bash
50-
zig fetch --save=curl https://github.com/jiacai2050/zig-curl/archive/${COMMIT}.tar.gz
50+
zig fetch --save=curl https://github.com/jiacai2050/zig-curl/archive/refs/tags/${TAG}.zip
5151
#+end_src
5252

53-
Replace ~${COMMIT}~ with a real one, then in your =build.zig=, import the module like this:
53+
The latest tag can be found on [[https://github.com/jiacai2050/zig-curl/releases/][release page]].
54+
55+
After fetch, import =curl= like this in your =build.zig=:
5456
#+begin_src zig
5557
const dep_curl = b.dependency("curl", .{});
5658
exe.root_module.addImport("curl", dep_curl.module("curl"));

build.zig

Lines changed: 34 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,11 @@ const std = @import("std");
22
const Build = std.Build;
33
const Step = Build.Step;
44
const Module = Build.Module;
5+
const Allocator = std.mem.Allocator;
56

67
const MODULE_NAME = "curl";
78

8-
pub fn build(b: *Build) void {
9+
pub fn build(b: *Build) !void {
910
const target = b.standardTargetOptions(.{});
1011
const optimize = b.standardOptimizeOption(.{});
1112
const link_vendor = b.option(bool, "link_vendor", "Whether link to vendored libcurl (default: true)") orelse true;
@@ -16,6 +17,12 @@ pub fn build(b: *Build) void {
1617
.target = target,
1718
.optimize = optimize,
1819
});
20+
const manifest = try parseManifest(b);
21+
defer manifest.deinit(b.allocator);
22+
23+
const opt = b.addOptions();
24+
opt.addOption([]const u8, "version", manifest.version);
25+
module.addImport("build_info", opt.createModule());
1926

2027
var libcurl: ?*Step.Compile = null;
2128
if (link_vendor) {
@@ -112,3 +119,29 @@ fn addExample(
112119
);
113120
run_step.dependOn(&b.addRunArtifact(exe).step);
114121
}
122+
123+
const Manifest = struct {
124+
version: []const u8,
125+
126+
fn deinit(self: Manifest, allocator: Allocator) void {
127+
allocator.free(self.version);
128+
}
129+
};
130+
131+
fn parseManifest(b: *Build) !Manifest {
132+
const input = @embedFile("build.zig.zon");
133+
var status: std.zon.parse.Status = .{};
134+
defer status.deinit(b.allocator);
135+
const parsed = std.zon.parse.fromSlice(
136+
Manifest,
137+
b.allocator,
138+
input,
139+
&status,
140+
.{ .free_on_error = true, .ignore_unknown_fields = true },
141+
) catch |err| {
142+
std.debug.print("Parse status: {any}\n", .{status});
143+
return err;
144+
};
145+
146+
return parsed;
147+
}

build.zig.zon

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
.{
22
.name = .curl,
33
.fingerprint = 0x3e01b4de1538b3f,
4-
.version = "0.1.1",
4+
.version = "0.2.0",
55
.paths = .{
66
"src",
77
"libs",

examples/advanced.zig

Lines changed: 21 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -26,11 +26,11 @@ fn putWithCustomHeader(allocator: Allocator, easy: Easy) !void {
2626
;
2727

2828
const headers = blk: {
29-
var h = try easy.createHeaders();
29+
var h: Easy.Headers = .{};
3030
errdefer h.deinit();
31-
try h.add("content-type", "application/json");
32-
try h.add("user-agent", UA);
33-
try h.add("Authorization", "Basic YWxhZGRpbjpvcGVuc2VzYW1l");
31+
try h.add("content-type: application/json");
32+
try h.add(std.fmt.comptimePrint("user-agent: {s}", .{UA}));
33+
try h.add("Authorization: Basic YWxhZGRpbjpvcGVuc2VzYW1l");
3434
break :blk h;
3535
};
3636
defer headers.deinit();
@@ -40,20 +40,21 @@ fn putWithCustomHeader(allocator: Allocator, easy: Easy) !void {
4040
try easy.setMethod(.PUT);
4141
try easy.setVerbose(true);
4242
try easy.setPostFields(body);
43-
var buf = curl.Buffer.init(allocator);
43+
var buf = curl.DynamicBuffer.init(allocator);
44+
defer buf.deinit();
45+
4446
try easy.setWritedata(&buf);
45-
try easy.setWritefunction(curl.bufferWriteCallback);
47+
try easy.setWritefunction(Easy.dynamicBufferWriteCallback);
4648

4749
var resp = try easy.perform();
48-
resp.body = buf;
4950
defer resp.deinit();
5051

5152
std.debug.print("Status code: {d}\nBody: {s}\n", .{
5253
resp.status_code,
53-
resp.body.?.items,
54+
buf.items,
5455
});
5556

56-
const parsed = try std.json.parseFromSlice(Response, allocator, resp.body.?.items, .{
57+
const parsed = try std.json.parseFromSlice(Response, allocator, buf.items, .{
5758
.ignore_unknown_fields = true,
5859
});
5960
defer parsed.deinit();
@@ -86,7 +87,7 @@ fn putWithCustomHeader(allocator: Allocator, easy: Easy) !void {
8687
}
8788
}
8889

89-
fn postMutliPart(easy: Easy) !void {
90+
fn postMultiPart(allocator: Allocator, easy: Easy) !void {
9091
// Reset old options, e.g. headers.
9192
easy.reset();
9293

@@ -101,28 +102,29 @@ fn postMutliPart(easy: Easy) !void {
101102
try easy.setMethod(.PUT);
102103
try easy.setMultiPart(multi_part);
103104
try easy.setVerbose(true);
104-
var buf = curl.Buffer.init(easy.allocator);
105+
var buf = curl.DynamicBuffer.init(allocator);
106+
defer buf.deinit();
107+
105108
try easy.setWritedata(&buf);
106-
try easy.setWritefunction(curl.bufferWriteCallback);
109+
try easy.setWritefunction(curl.Easy.dynamicBufferWriteCallback);
107110

108111
var resp = try easy.perform();
109-
resp.body = buf;
110112
defer resp.deinit();
111113

112-
std.debug.print("resp:{s}\n", .{resp.body.?.items});
114+
std.debug.print("resp:{s}\n", .{buf.items});
113115
}
114116

115117
pub fn main() !void {
116-
const allocator = std.heap.page_allocator;
118+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
119+
defer if (gpa.deinit() != .ok) @panic("leak");
120+
const allocator = gpa.allocator();
117121

118122
const ca_bundle = try curl.allocCABundle(allocator);
119123
defer ca_bundle.deinit();
120-
const easy = try Easy.init(allocator, .{
121-
.ca_bundle = ca_bundle,
122-
});
124+
const easy = try Easy.init(.{ .ca_bundle = ca_bundle });
123125
defer easy.deinit();
124126

125127
println("PUT with custom header demo");
126128
try putWithCustomHeader(allocator, easy);
127-
try postMutliPart(easy);
129+
try postMultiPart(allocator, easy);
128130
}

examples/basic.zig

Lines changed: 42 additions & 62 deletions
Original file line numberDiff line numberDiff line change
@@ -8,93 +8,73 @@ const Easy = curl.Easy;
88
const LOCAL_SERVER_ADDR = "http://localhost:8182";
99

1010
fn get(allocator: Allocator, easy: Easy) !void {
11-
try easy.setVerbose(true);
12-
const resp = try easy.get("https://httpbin.org/anything");
13-
defer resp.deinit();
14-
15-
const body = resp.body.?.items;
16-
std.debug.print("Status code: {d}\nBody: {s}\n", .{
17-
resp.status_code,
18-
body,
19-
});
20-
21-
const Response = struct {
22-
headers: struct {
23-
Host: []const u8,
24-
},
25-
method: []const u8,
26-
};
27-
const parsed = try std.json.parseFromSlice(Response, allocator, body, .{
28-
.ignore_unknown_fields = true,
29-
});
30-
defer parsed.deinit();
31-
32-
try std.testing.expectEqualDeep(parsed.value, Response{
33-
.headers = .{ .Host = "httpbin.org" },
34-
.method = "GET",
35-
});
11+
{
12+
println("GET with allocator");
13+
const resp = try easy.fetchAlloc("https://httpbin.org/anything", allocator, .{});
14+
defer resp.deinit();
15+
16+
const body = resp.body.?.slice();
17+
std.debug.print("Status code: {d}\nBody: {s}\n", .{
18+
resp.status_code,
19+
body,
20+
});
21+
}
22+
23+
{
24+
println("GET with fixed buffer");
25+
var buffer: [1024]u8 = undefined;
26+
const resp = try easy.fetch("https://httpbin.org/anything", &buffer, .{});
27+
defer resp.deinit();
28+
const body = resp.body.?.slice();
29+
std.debug.print("Status code: {d}\nBody: {s}\n", .{
30+
resp.status_code,
31+
body,
32+
});
33+
}
3634
}
3735

3836
fn post(allocator: Allocator, easy: Easy) !void {
3937
const payload =
4038
\\{"name": "John", "age": 15}
4139
;
42-
try easy.setVerbose(false);
43-
const resp = try easy.post("https://httpbin.org/anything", "application/json", payload);
40+
const resp = try easy.fetchAlloc(
41+
"https://httpbin.org/anything",
42+
allocator,
43+
.{
44+
.method = .POST,
45+
.body = payload,
46+
.headers = &.{
47+
"Content-Type: application/json",
48+
},
49+
},
50+
);
4451
defer resp.deinit();
4552

4653
std.debug.print("Status code: {d}\nBody: {s}\n", .{
4754
resp.status_code,
48-
resp.body.?.items,
49-
});
50-
51-
const Response = struct {
52-
headers: struct {
53-
@"Content-Type": []const u8,
54-
},
55-
json: struct {
56-
name: []const u8,
57-
age: u32,
58-
},
59-
method: []const u8,
60-
};
61-
const parsed = try std.json.parseFromSlice(Response, allocator, resp.body.?.items, .{ .ignore_unknown_fields = true });
62-
defer parsed.deinit();
63-
64-
try std.testing.expectEqualDeep(parsed.value, Response{
65-
.headers = .{ .@"Content-Type" = "application/json" },
66-
.json = .{ .name = "John", .age = 15 },
67-
.method = "POST",
55+
resp.body.?.slice(),
6856
});
6957
}
7058

7159
fn upload(allocator: Allocator, easy: Easy) !void {
7260
const path = "LICENSE";
73-
const resp = try easy.upload(LOCAL_SERVER_ADDR ++ "/anything", path);
74-
const Response = struct {
75-
method: []const u8,
76-
body_len: usize,
77-
};
78-
const parsed = try std.json.parseFromSlice(Response, allocator, resp.body.?.items, .{ .ignore_unknown_fields = true });
79-
defer parsed.deinit();
80-
81-
try std.testing.expectEqualDeep(parsed.value, Response{
82-
.body_len = 1086,
83-
.method = "PUT",
84-
});
61+
const resp = try easy.uploadAlloc(LOCAL_SERVER_ADDR ++ "/anything", path, allocator);
62+
defer resp.deinit();
8563

8664
std.debug.print("Status code: {d}\nBody: {s}\n", .{
8765
resp.status_code,
88-
resp.body.?.items,
66+
resp.body.?.slice(),
8967
});
9068
}
9169

9270
pub fn main() !void {
93-
const allocator = std.heap.page_allocator;
71+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
72+
defer if (gpa.deinit() != .ok) @panic("leak");
73+
const allocator = gpa.allocator();
9474

9575
const ca_bundle = try curl.allocCABundle(allocator);
9676
defer ca_bundle.deinit();
97-
const easy = try Easy.init(allocator, .{
77+
const easy = try Easy.init(.{
9878
.ca_bundle = ca_bundle,
9979
});
10080
defer easy.deinit();

examples/header.zig

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,11 @@ fn iterateHeaders(easy: Easy) !void {
77
// Reset old options, e.g. headers.
88
easy.reset();
99

10-
const resp = try easy.get("https://httpbin.org/response-headers?X-Foo=1&X-Foo=2&X-Foo=3");
10+
const resp = try easy.fetch(
11+
"https://httpbin.org/response-headers?X-Foo=1&X-Foo=2&X-Foo=3",
12+
null,
13+
.{},
14+
);
1115
defer resp.deinit();
1216

1317
std.debug.print("Iterating all headers...\n", .{});
@@ -35,7 +39,7 @@ fn iterateRedirectedHeaders(easy: Easy) !void {
3539
easy.reset();
3640

3741
try easy.setFollowLocation(true);
38-
const resp = try easy.get("https://httpbin.org/redirect/1");
42+
const resp = try easy.fetch("https://httpbin.org/redirect/1", null, .{});
3943
defer resp.deinit();
4044

4145
const redirects = try resp.getRedirectCount();
@@ -63,13 +67,13 @@ fn iterateRedirectedHeaders(easy: Easy) !void {
6367
}
6468

6569
pub fn main() !void {
66-
const allocator = std.heap.page_allocator;
70+
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
71+
defer if (gpa.deinit() != .ok) @panic("leak");
72+
const allocator = gpa.allocator();
6773

6874
const ca_bundle = try curl.allocCABundle(allocator);
6975
defer ca_bundle.deinit();
70-
const easy = try Easy.init(allocator, .{
71-
.ca_bundle = ca_bundle,
72-
});
76+
const easy = try Easy.init(.{ .ca_bundle = ca_bundle });
7377
defer easy.deinit();
7478

7579
if (comptime !curl.hasParseHeaderSupport()) {

0 commit comments

Comments
 (0)