summaryrefslogtreecommitdiff
path: root/libs/curl/curl.zig
blob: 4a26eed60c855fb6016e3060e8b847941238f45f (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
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;
        }
    }
};