Ejemplo de uso de una librería: argonaut

Ejemplo de uso de una librería: argonaut

Vamos a usar una librería para añadir una funcionalidad muy común cuando creamos herramientas de línea de comandos: parsear argumentos.

En https://zigistry.dev/ puedes buscar librerías de todo tipo. De ahí he elegido una que me ha parecido intuitiva para este propósito:

Se llama argonaut: https://codeberg.org/OhMyDitzzy/argonaut.

La descripción de la librería a fecha de diciembre de 2025 es la siguiente:

Resumen

Un parser de argumentos de línea de comandos flexible y potente para Zig, inspirado en la librería argparse de Go. Ofrece una API clara e intuitiva para construir aplicaciones CLI complejas, con soporte para subcomandos, varios tipos de argumentos, validación y más.

Características

Instalación

Añade la dependencia en tu build.zig.zon ejecutando el siguiente comando:

zig fetch --save=argonaut

 https://codeberg.org/OhMyDitzzy/argonaut/archive/master.tar.gz

Luego, en tu build.zig:

  exe.root_module.addImport(

    "argonaut",

    b.dependency("argonaut", .{ .target = target, 

         .optimize = optimize }).module("argonaut"));

Una vez que hemos leído la descripción de la librería vamos a crear nuestro programa que llamaremos argdemo:


mkdir argdemo;

cd argdemo;

zig init  --minimal 


Zig creará una configuración mínima. Inmediatamente después, modificamos los ficheros build.zig.zon y build.zig.

Como hemos visto del README.md de la librería ejecutamos:


zig fetch --save=argonaut https://codeberg.org/OhMyDitzzy/argonaut/archive/master.tar.gz


Este comando bajará la librería y modificará el fichero build.zig.zon

Ficheros de configuración y el programa

build.zig.zon

  .{

    .name = .argdemo,

    .version = "0.0.1",

    .dependencies = .{

        .argonaut = .{

            .url = "https://codeberg.org/…",

            .hash = "...",

        },

    },

    .minimum_zig_version = "0.15.2",

    .paths = .{

        "build.zig",

        "build.zig.zon",

        "src",

    },

    .fingerprint = <aquí zig genera un fingerprint>,

}

Zig fetch anotará como dependencia la librería pero nosotros necesitamos poner los .paths de los ficheros.

build.zig

  const std = @import("std");

  pub fn build(b: *std.Build) void {

    const target = b.standardTargetOptions(.{});

    const optimize = b.standardOptimizeOption(.{});

    const exe = b.addExecutable(.{

        .name = "argdemo",

        .root_module = b.createModule(.{

            .root_source_file = b.path("src/main.zig"),

            .target = target,

            .optimize = optimize,

        }),

    });

    exe.root_module.addImport("argonaut", b.dependency("argonaut", .{

        .target = target,

        .optimize = optimize,

    }).module("argonaut"));

    b.installArtifact(exe);

    const run_step = b.step("run", "Run the app");

    const run_cmd = b.addRunArtifact(exe);

    if (b.args) |args| {

        run_cmd.addArgs(args);

    }

    run_step.dependOn(&run_cmd.step);

    run_cmd.step.dependOn(b.getInstallStep());

}

Después de modificar estos ficheros creamos el archivo de código fuente del propio programa src/main.zig:

src/main.zig

  const std = @import("std");

  const print = std.debug.print;

  // Seguimos las instrucciones del repositorio de argonaut

  const argsparse = @import("argonaut");

  pub fn main() !void {

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};

    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    // Create a parser with a program name and description

    const parser = try argsparse.newParser(

        allocator,

        "Argdemo",

        "Aplicación de prueba",

    );

    defer parser.deinit();

    var name_opts = argsparse.Options{

        .required = true,

        .help = "Tu nombre",

    };

    const s_name = try parser.string(

        "n",

        "name",

        &name_opts,

    );

    // creamos una función para validar la edad

    const fn_validate_age = struct {

        // *const fn (args: []const []const u8) anyerror!void;

        fn _(args: []const []const u8) !void {

            if (args.len == 0 or args.len > 1) return;

            const n_age = std.fmt.parseInt(

                i64,

                args[0],

                10,

            ) catch return error.InvalidNumber;

            if (n_age < 18)

                return error.AgeTooLow

            else if (n_age > 120) return error.AgeTooHigh else return;

        }

    }._;

    var age_opts = argsparse.Options{

        .required = false,

        .help = "Tu edad como número. Solo se acepta entre 18 y 120",

        .default_int = 33,

        .validate = fn_validate_age,

    };

    const n_age = try parser.int(

        "a",

        "age",

        &age_opts,

    );

    const args = try std.process.argsAlloc(allocator);

    defer std.process.argsFree(allocator, args);

    _ = parser.parse(args) catch {

        const usage_text = try parser.usage(null);

        defer allocator.free(usage_text);

        std.debug.print("{s}", .{usage_text});

        std.process.exit(1);

    };

    print("Hola: {s}, tu edad: {d} \n", .{

        s_name.*,

        n_age.*,

    });

  }

El uso de la librería es sencillo si seguimos las instrucciones de la propia página del repositorio. Importamos la librería igual que hacemos habitualmente con std.

Copiamos el código tal y como viene explicado en el repositorio para un uso básico de argonaut:

    var gpa = std.heap.GeneralPurposeAllocator(.{}){};

    defer _ = gpa.deinit();

    const allocator = gpa.allocator();

    // Create a parser with a program name and description

    const parser = try argsparse.newParser(

        allocator,

        "Argdemo",

        "Aplicación de prueba",

    );

    defer parser.deinit();

La única parte más “complicada” es si queremos crear una función de validación (muy parecido a lo que hemos visto en los capítulos anteriores:

    // creamos una función para validar la edad

    const fn_validate_age = struct {

        // *const fn (args: []const []const u8) anyerror!void;

        fn _(args: []const []const u8) !void {       

        // ...

    }._;

    var age_opts = argsparse.Options{ .validate = fn_validate_age };

Cuando ejecutamos el programa obtenemos:

$ zig build && zig-out/bin/argdemo --name Ziguana --age 140

usage: Argdemo [-a|--age <integer>] -n|--name "<value>" [-h|--help]

               Aplicación de prueba

Arguments:

  -a  --age   Tu edad como número. Solo se acepta entre 18 y 120. Default: 33

  -n  --name  Tu nombre

  -h  --help  Print help information

Crear un proyecto: zig init
Resumen del capítulo
© 2025 Zen of Zig