pub const c = @cImport(@cInclude("curl/curl.h")); const std = @import("std"); const Allocator = std.mem.Allocator; const ArrayList = std.ArrayList; const File = std.fs.File; pub fn easyDownload(allocator: Allocator, url: [:0]const u8) ![]u8 { var handle = try Easy.init(); defer handle.deinit(); var buf = ArrayList(u8).init(allocator); defer buf.deinit(); try handle.setopt(.url, url.ptr); try handle.setopt(.follow_location, true); try handle.setopt(.write_function, easyDownloadCb); try handle.setopt(.write_data, &buf); try handle.perform(); return buf.toOwnedSlice(); } fn easyDownloadCb(ptr: [*]const u8, size: usize, nmemb: usize, buf: *ArrayList(u8)) usize { std.debug.assert(size == 1); const slice = ptr[0..nmemb]; buf.appendSlice(slice) catch |err| { std.log.err("in easyDownloadCb: {}", .{err}); return 0; }; return nmemb; } pub fn easyDownloadToFile(file: *File, url: [:0]const u8) !void { var handle = try Easy.init(); defer handle.deinit(); const writer = file.writer(); try handle.setopt(.url, url.ptr); try handle.setopt(.follow_location, true); try handle.setopt(.write_function, easyDownloadToFileCb); try handle.setopt(.write_data, &writer); try handle.perform(); } fn easyDownloadToFileCb(ptr: [*]const u8, size: usize, nmemb: usize, writer: *const File.Writer) usize { std.debug.assert(size == 1); const slice = ptr[0..nmemb]; writer.writeAll(slice) catch |err| { std.log.err("in easyDownloadToFileCb: {}", .{err}); return 0; }; return nmemb; } pub const Easy = struct { raw: *c.CURL, pub const Option = enum(c.CURLoption) { follow_location = c.CURLOPT_FOLLOWLOCATION, url = c.CURLOPT_URL, write_data = c.CURLOPT_WRITEDATA, write_function = c.CURLOPT_WRITEFUNCTION, }; pub fn init() !Easy { if (c.curl_easy_init()) |raw| { return Easy{ .raw = raw }; } else { return error.CurlError; } } pub fn deinit(self: *Easy) void { c.curl_easy_cleanup(self.raw); self.* = undefined; } pub fn perform(self: *Easy) !void { const errc = c.curl_easy_perform(self.raw); if (errc != c.CURLE_OK) { std.log.err("Curl: {s}", .{c.curl_easy_strerror(errc)}); return error.CurlError; } } pub fn setopt(self: *Easy, option: Option, param: anytype) !void { const option_raw = @enumToInt(option); const errc = c.curl_easy_setopt(self.raw, option_raw, param); if (errc != c.CURLE_OK) { std.log.err("Curl: {s}", .{c.curl_easy_strerror(errc)}); return error.CurlError; } } };