第20章: 実践プロジェクト

学習目標

  • プロジェクト構成のベストプラクティス
  • build.zigの活用
  • テストとドキュメント
  • 実践的なCLIツール開発

プロジェクト構成

標準的なディレクトリ構造

my_project/
├── build.zig          # ビルド設定
├── build.zig.zon      # 依存関係
├── src/
│   ├── main.zig      # エントリーポイント
│   ├── cli.zig       # CLI処理
│   ├── core.zig      # コアロジック
│   └── utils.zig     # ユーティリティ
├── tests/
│   └── tests.zig     # テスト
└── README.md         # ドキュメント

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 = "mytool",
        .root_source_file = .{ .path = "src/main.zig" },
        .target = target,
        .optimize = optimize,
    });

    b.installArtifact(exe);

    const run_cmd = b.addRunArtifact(exe);
    const run_step = b.step("run", "Run the app");
    run_step.dependOn(&run_cmd.step);

    const tests = b.addTest(.{
        .root_source_file = .{ .path = "tests/tests.zig" },
        .target = target,
        .optimize = optimize,
    });

    const test_step = b.step("test", "Run tests");
    test_step.dependOn(&b.addRunArtifact(tests).step);
}

CLIツールの実装例

コマンドライン引数の解析

const std = @import("std");

pub const Args = struct {
    command: Command,
    verbose: bool,
    output: ?[]const u8,

    pub const Command = enum {
        help,
        version,
        process,
    };

    pub fn parse(allocator: std.mem.Allocator) !Args {
        var args = try std.process.argsWithAllocator(allocator);
        defer args.deinit();

        _ = args.skip(); // プログラム名をスキップ

        var result = Args{
            .command = .help,
            .verbose = false,
            .output = null,
        };

        while (args.next()) |arg| {
            if (std.mem.eql(u8, arg, "--help")) {
                result.command = .help;
            } else if (std.mem.eql(u8, arg, "--version")) {
                result.command = .version;
            } else if (std.mem.eql(u8, arg, "--verbose")) {
                result.verbose = true;
            } else if (std.mem.eql(u8, arg, "--output")) {
                result.output = args.next() orelse return error.MissingValue;
            }
        }

        return result;
    }
};

ベストプラクティス

エラーハンドリング

pub const AppError = error{
    FileNotFound,
    InvalidFormat,
    PermissionDenied,
};

pub fn processFile(path: []const u8) AppError!void {
    // エラー処理を明示的に
}

テスト

const std = @import("std");
const expect = std.testing.expect;

test "basic functionality" {
    const result = add(2, 3);
    try expect(result == 5);
}

まとめ

この章で学んだこと:

  • プロジェクト構成
  • build.zigの活用
  • CLIツール開発
  • テストとドキュメント

Zig Foundationsコースの完了おめでとうございます!