summaryrefslogtreecommitdiffstats
path: root/src/main.zig
blob: 77baae882fafcc74f60d92cf71d0895f0b313c08 (plain)
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
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
//! A one-shot CLI over the library, mostly so `zig build run` can confirm that
//! an instance answers.
//!
//!     cepis 01001000
//!     cepis --base-url http://localhost:8080 01001000 20040020
//!     cepis --health

const std = @import("std");
const cepis = @import("cepis");

const usage =
    \\usage: cepis [--base-url URL] [--health] [CEP ...]
    \\
    \\  --base-url URL  where the API lives (default https://cep.is)
    \\  --health        query GET /health instead of looking anything up
    \\  --help          show this message
    \\
;

pub fn main(init: std.process.Init) !u8 {
    const gpa = init.gpa;
    const io = init.io;
    const arena = init.arena.allocator();

    var stdout_buffer: [4096]u8 = undefined;
    var stdout: std.Io.File.Writer = .init(.stdout(), io, &stdout_buffer);
    const out = &stdout.interface;
    defer out.flush() catch {};

    const args = try init.minimal.args.toSlice(arena);

    var base_url: []const u8 = cepis.default_url;
    var want_health = false;
    var ceps: std.ArrayList([]const u8) = .empty;
    defer ceps.deinit(gpa);

    var i: usize = 1;
    while (i < args.len) : (i += 1) {
        const arg = args[i];
        if (std.mem.eql(u8, arg, "--help") or std.mem.eql(u8, arg, "-h")) {
            try out.writeAll(usage);
            return 0;
        } else if (std.mem.eql(u8, arg, "--health")) {
            want_health = true;
        } else if (std.mem.eql(u8, arg, "--base-url")) {
            i += 1;
            if (i >= args.len) {
                try out.writeAll("--base-url needs a value\n");
                return 2;
            }
            base_url = args[i];
        } else {
            try ceps.append(gpa, arg);
        }
    }

    if (!want_health and ceps.items.len == 0) {
        try out.writeAll(usage);
        return 2;
    }

    var client: cepis.Client = try .init(gpa, io, .{ .base_url = base_url });
    defer client.deinit();

    var diag: cepis.Diagnostics = .{};

    if (want_health) {
        var health = client.health(.{ .diagnostics = &diag }) catch |err| {
            try out.print("health failed: {t}: {s}\n", .{ err, diag.message() });
            return 1;
        };
        defer health.deinit(gpa);
        try out.print("{s} dne={s} ceps={d} geo={d} gerada_em={s}\n", .{
            health.status, health.dne, health.ceps, health.geo, health.base_gerada_em,
        });
        return 0;
    }

    if (ceps.items.len == 1) {
        var address = client.lookup(ceps.items[0], .{ .diagnostics = &diag }) catch |err| {
            try out.print("{s}: {t}: {s}\n", .{ ceps.items[0], err, diag.message() });
            // Not an address — the lookup failed. Only where the number would
            // have landed, which is usually the useful thing to say back.
            if (diag.faixa()) |faixa| {
                try out.print("  would be in {s}/{s}, which has {d} CEPs between {s} and {s}\n", .{
                    faixa.localidade, faixa.uf, faixa.ceps, faixa.cep_ini, faixa.cep_fim,
                });
            }
            return 1;
        };
        defer address.deinit(gpa);
        try printAddress(out, address);
        return 0;
    }

    const found = client.lookupMany(ceps.items, .{ .diagnostics = &diag }) catch |err| {
        try out.print("batch failed: {t}: {s}\n", .{ err, diag.message() });
        return 1;
    };
    defer cepis.freeAddresses(gpa, found);

    for (ceps.items, found) |requested, maybe_address| {
        if (maybe_address) |address| {
            try printAddress(out, address);
        } else {
            try out.print("{s}: not found\n", .{requested});
        }
    }
    return 0;
}

fn printAddress(out: *std.Io.Writer, address: cepis.Address) !void {
    var buffer: [16]u8 = undefined;
    try out.print("{s} {s}, {s} — {s}/{s}", .{
        address.formattedCep(&buffer),
        address.logradouro,
        address.bairro,
        address.localidade,
        address.uf,
    });
    if (address.geo) |geo| {
        try out.print(" [{d:.6},{d:.6} ±{d}m {s}]", .{ geo.lat, geo.lon, geo.raio_m, geo.precisao });
    }
    try out.writeAll("\n");
}