std.Uri em Zig — Referência e Exemplos

std.Uri — Parsing e Manipulação de URIs

O tipo std.Uri implementa o parsing de URIs conforme a RFC 3986. Ele decompõe uma string de URI em seus componentes — scheme, host, porta, path, query e fragment — sem realizar nenhuma alocação de memória, retornando slices que apontam para a string original.

Visão Geral

const std = @import("std");
const Uri = std.Uri;

Estrutura do Tipo

pub const Uri = struct {
    scheme: []const u8,
    user: ?Component = null,
    password: ?Component = null,
    host: ?Component = null,
    port: ?u16 = null,
    path: Component,
    query: ?Component = null,
    fragment: ?Component = null,

    pub const Component = union(enum) {
        raw: []const u8,
        percent_encoded: []const u8,
    };
};

Anatomia de uma URI

  https://usuario:senha@exemplo.com:8080/caminho/recurso?q=busca#secao
  \___/   \_____/ \___/ \_________/ \__/ \______________/ \_____/ \____/
  scheme  user    pass   host       port path            query   fragment

Funções Principais

// Parse de uma string URI
pub fn parse(uri_str: []const u8) !Uri

// Resolve uma referência relativa contra uma base
pub fn resolve(base: Uri, reference: Uri) Uri

// Formata a URI como string
pub fn format(self: Uri, comptime fmt: []const u8, options: std.fmt.FormatOptions, writer: anytype) !void

// Decodifica percent-encoding
pub fn percentDecode(allocator: Allocator, input: []const u8) ![]u8

// Codifica para percent-encoding
pub fn percentEncode(input: []const u8) PercentEncoded

Exemplo 1: Parsing de URLs

const std = @import("std");

pub fn main() !void {
    const stdout = std.io.getStdOut().writer();

    const urls = [_][]const u8{
        "https://api.exemplo.com:443/v2/usuarios?page=1&limit=10#resultados",
        "http://localhost:8080/health",
        "postgres://admin:s3nh4@db.interno:5432/meuapp",
        "file:///home/usuario/documento.txt",
    };

    for (urls) |url_str| {
        const uri = std.Uri.parse(url_str) catch |err| {
            try stdout.print("Erro ao parsear '{s}': {}\n", .{ url_str, err });
            continue;
        };

        try stdout.print("URI: {s}\n", .{url_str});
        try stdout.print("  Scheme: {s}\n", .{uri.scheme});

        if (uri.host) |host| {
            switch (host) {
                .raw => |raw| try stdout.print("  Host:   {s}\n", .{raw}),
                .percent_encoded => |enc| try stdout.print("  Host:   {s} (encoded)\n", .{enc}),
            }
        }

        if (uri.port) |porta| {
            try stdout.print("  Porta:  {d}\n", .{porta});
        }

        switch (uri.path) {
            .raw => |raw| try stdout.print("  Path:   {s}\n", .{raw}),
            .percent_encoded => |enc| try stdout.print("  Path:   {s}\n", .{enc}),
        }

        if (uri.query) |query| {
            switch (query) {
                .raw => |raw| try stdout.print("  Query:  {s}\n", .{raw}),
                .percent_encoded => |enc| try stdout.print("  Query:  {s}\n", .{enc}),
            }
        }

        if (uri.fragment) |frag| {
            switch (frag) {
                .raw => |raw| try stdout.print("  Frag:   {s}\n", .{raw}),
                .percent_encoded => |enc| try stdout.print("  Frag:   {s}\n", .{enc}),
            }
        }

        try stdout.writeAll("\n");
    }
}

Exemplo 2: Construtor de URLs

const std = @import("std");

fn construirUrl(
    allocator: std.mem.Allocator,
    base: []const u8,
    caminho: []const u8,
    params: []const [2][]const u8,
) ![]u8 {
    var buf = std.ArrayList(u8).init(allocator);
    defer buf.deinit();
    const writer = buf.writer();

    try writer.writeAll(base);

    // Garante barra entre base e caminho
    if (base.len > 0 and base[base.len - 1] != '/') {
        if (caminho.len == 0 or caminho[0] != '/') {
            try writer.writeByte('/');
        }
    }
    try writer.writeAll(caminho);

    // Query parameters
    if (params.len > 0) {
        try writer.writeByte('?');
        for (params, 0..) |par, i| {
            if (i > 0) try writer.writeByte('&');
            try writer.writeAll(par[0]);
            try writer.writeByte('=');
            try writer.writeAll(par[1]);
        }
    }

    return try buf.toOwnedSlice();
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const stdout = std.io.getStdOut().writer();

    const params = [_][2][]const u8{
        .{ "page", "1" },
        .{ "limit", "20" },
        .{ "sort", "nome" },
    };

    const url = try construirUrl(
        allocator,
        "https://api.exemplo.com",
        "v2/usuarios",
        &params,
    );
    defer allocator.free(url);

    try stdout.print("URL construída: {s}\n", .{url});

    // Verifica parseando
    const uri = try std.Uri.parse(url);
    try stdout.print("Scheme: {s}\n", .{uri.scheme});
    if (uri.query) |q| {
        switch (q) {
            .raw => |raw| try stdout.print("Query: {s}\n", .{raw}),
            .percent_encoded => |enc| try stdout.print("Query: {s}\n", .{enc}),
        }
    }
}

Exemplo 3: Extraindo Parâmetros de Query

const std = @import("std");

fn parseQueryParams(
    query: []const u8,
    allocator: std.mem.Allocator,
) !std.StringHashMap([]const u8) {
    var params = std.StringHashMap([]const u8).init(allocator);
    errdefer params.deinit();

    var pares = std.mem.splitScalar(u8, query, '&');
    while (pares.next()) |par| {
        if (std.mem.indexOf(u8, par, "=")) |sep| {
            const chave = par[0..sep];
            const valor = par[sep + 1 ..];
            try params.put(chave, valor);
        }
    }

    return params;
}

pub fn main() !void {
    var gpa = std.heap.GeneralPurposeAllocator(.{}){};
    defer _ = gpa.deinit();
    const allocator = gpa.allocator();

    const url = "https://loja.com/busca?q=teclado+mecanico&categoria=perifericos&preco_max=500&ordenar=relevancia";

    const uri = try std.Uri.parse(url);
    const stdout = std.io.getStdOut().writer();

    if (uri.query) |query_comp| {
        const query_str = switch (query_comp) {
            .raw => |raw| raw,
            .percent_encoded => |enc| enc,
        };

        var params = try parseQueryParams(query_str, allocator);
        defer params.deinit();

        try stdout.writeAll("Parâmetros de busca:\n");
        var it = params.iterator();
        while (it.next()) |entry| {
            try stdout.print("  {s} = {s}\n", .{
                entry.key_ptr.*,
                entry.value_ptr.*,
            });
        }

        // Acesso direto
        const busca = params.get("q") orelse "(vazio)";
        try stdout.print("\nBusca: {s}\n", .{busca});
    }
}

Módulos Relacionados

Tutoriais e Receitas Relacionados

Continue aprendendo Zig

Explore mais tutoriais e artigos em português para dominar a linguagem Zig.