Cheatsheet: Adapter em Zig

Adapter em Zig

O padrão Adapter permite que interfaces incompatíveis trabalhem juntas, convertendo a interface de uma struct em outra que o código cliente espera. Em Zig, esse padrão é muito usado para envolver APIs C em interfaces idiomáticas Zig, ou para unificar diferentes implementações sob uma interface comum.

Quando Usar

  • Wrapping de bibliotecas C para interface idiomática Zig
  • Unificar diferentes fontes de dados sob mesma interface
  • Converter entre formatos de dados incompatíveis
  • Integrar bibliotecas de terceiros com API diferente da esperada

Adapter para API C

const std = @import("std");
const c = @cImport(@cInclude("stdio.h"));

// API C retorna ponteiro ou NULL, usa errno
// Adapter converte para interface Zig com error unions

const ArquivoAdaptado = struct {
    handle: *c.FILE,

    const Erro = error{
        FalhaAoAbrir,
        FalhaAoLer,
        FalhaAoEscrever,
    };

    pub fn abrir(caminho: [:0]const u8, modo: [:0]const u8) Erro!ArquivoAdaptado {
        const handle = c.fopen(caminho.ptr, modo.ptr);
        if (handle == null) return error.FalhaAoAbrir;
        return .{ .handle = handle.? };
    }

    pub fn fechar(self: *ArquivoAdaptado) void {
        _ = c.fclose(self.handle);
    }

    pub fn escrever(self: *ArquivoAdaptado, dados: []const u8) Erro!usize {
        const escrito = c.fwrite(dados.ptr, 1, dados.len, self.handle);
        if (escrito == 0) return error.FalhaAoEscrever;
        return escrito;
    }

    pub fn ler(self: *ArquivoAdaptado, buffer: []u8) Erro!usize {
        const lido = c.fread(buffer.ptr, 1, buffer.len, self.handle);
        if (lido == 0 and c.ferror(self.handle) != 0) return error.FalhaAoLer;
        return lido;
    }
};

pub fn main() !void {
    var arquivo = try ArquivoAdaptado.abrir("/tmp/teste.txt", "w");
    defer arquivo.fechar();

    _ = try arquivo.escrever("Olá do adapter!\n");
}

Adapter de Interface Genérica

const std = @import("std");

// Interface comum
const FonteDados = struct {
    ptr: *anyopaque,
    lerFn: *const fn (*anyopaque, []u8) ?[]const u8,

    pub fn ler(self: FonteDados, buffer: []u8) ?[]const u8 {
        return self.lerFn(self.ptr, buffer);
    }
};

// Adaptar ArrayList para FonteDados
const ArrayListAdapter = struct {
    lista: *std.ArrayList([]const u8),
    indice: usize = 0,

    pub fn fonteDados(self: *ArrayListAdapter) FonteDados {
        return .{
            .ptr = @ptrCast(self),
            .lerFn = @ptrCast(&proximo),
        };
    }

    fn proximo(self: *ArrayListAdapter, buffer: []u8) ?[]const u8 {
        _ = buffer;
        if (self.indice >= self.lista.items.len) return null;
        const item = self.lista.items[self.indice];
        self.indice += 1;
        return item;
    }
};

// Adaptar slice estático para FonteDados
const SliceAdapter = struct {
    dados: []const []const u8,
    indice: usize = 0,

    pub fn fonteDados(self: *SliceAdapter) FonteDados {
        return .{
            .ptr = @ptrCast(self),
            .lerFn = @ptrCast(&proximo),
        };
    }

    fn proximo(self: *SliceAdapter, buffer: []u8) ?[]const u8 {
        _ = buffer;
        if (self.indice >= self.dados.len) return null;
        const item = self.dados[self.indice];
        self.indice += 1;
        return item;
    }
};

// Código cliente trabalha com FonteDados sem saber a origem
fn processarDados(fonte: FonteDados) void {
    var buf: [1024]u8 = undefined;
    while (fonte.ler(&buf)) |dado| {
        std.debug.print("Dado: {s}\n", .{dado});
    }
}

Quando Evitar

  • Quando as interfaces já são compatíveis
  • Quando um simples wrapper de função resolve
  • Se o adapter adiciona complexidade sem benefício claro
  • Quando é possível modificar a interface original

Veja Também

Continue aprendendo Zig

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