Last active
April 24, 2023 15:10
-
-
Save jiacai2050/0f3fa94eb145158a1e2267ec68eb6ac0 to your computer and use it in GitHub Desktop.
Tree(1) in Zig
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
//! Tree(1) in Zig | |
//! https://linux.die.net/man/1/tree | |
const std = @import("std"); | |
const process = std.process; | |
const fs = std.fs; | |
const mem = std.mem; | |
const testing = std.testing; | |
const VisitedEntry = struct { | |
name: []const u8, | |
level: usize, | |
}; | |
const VisitedList = std.ArrayList(VisitedEntry); | |
pub fn main() anyerror!void { | |
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator); | |
defer arena.deinit(); | |
const allocator = arena.allocator(); | |
const args = try process.argsAlloc(allocator); | |
defer process.argsFree(allocator, args); | |
const root_dir = if (args.len > 1) | |
args[1] | |
else | |
"."; | |
var visited = VisitedList.init(allocator); | |
try visited.append(.{ .name = root_dir, .level = 0 }); | |
var iter_dir = | |
try fs.cwd().openIterableDir(root_dir, .{}); | |
defer iter_dir.close(); | |
try walk(allocator, &iter_dir, &visited, 1); | |
var writer = std.io.bufferedWriter(std.io.getStdOut().writer()); | |
for (visited.items) |entry| { | |
_ = try writer.write(try genPrefix(allocator, entry.level)); | |
_ = try writer.write(entry.name); | |
_ = try writer.write("\n"); | |
} | |
try writer.flush(); | |
} | |
fn genPrefix(allocator: std.mem.Allocator, level: usize) ![]const u8 { | |
const prefix = "| "; | |
const base = "|--"; | |
return switch (level) { | |
0 => return try allocator.dupe(u8, ""), | |
1 => return try allocator.dupe(u8, base), | |
else => { | |
var out = try allocator.alloc(u8, (level - 1) * prefix.len + base.len); | |
for (0..level - 1) |i| { | |
mem.copy(u8, out[i * prefix.len ..], prefix); | |
} | |
mem.copy(u8, out[(level - 1) * prefix.len ..], base); | |
return out; | |
}, | |
}; | |
} | |
test "testing genPrefix" { | |
const allocator = testing.allocator; | |
const testcases = [_]std.meta.Tuple(&[_]type{ usize, []const u8 }){ | |
.{ 0, "" }, | |
.{ 1, "|--" }, | |
.{ 2, "| |--" }, | |
.{ 3, "| | |--" }, | |
}; | |
for (testcases) |case| { | |
const actual = try genPrefix(allocator, case.@"0"); | |
defer allocator.free(actual); | |
try testing.expectEqualStrings(case.@"1", actual); | |
} | |
} | |
fn walk( | |
allocator: mem.Allocator, | |
iter_dir: *fs.IterableDir, | |
visited: *VisitedList, | |
level: usize, | |
) !void { | |
var it = iter_dir.iterate(); | |
while (try it.next()) |entry| { | |
const dupe_name = try allocator.dupe(u8, entry.name); | |
switch (entry.kind) { | |
.Directory => { | |
try visited.append(.{ .name = dupe_name, .level = level }); | |
var sub_iter_dir = try iter_dir.dir.openIterableDir(entry.name, .{}); | |
defer sub_iter_dir.close(); | |
try walk( | |
allocator, | |
&sub_iter_dir, | |
visited, | |
level + 1, | |
); | |
}, | |
else => { | |
try visited.append(.{ .name = dupe_name, .level = level }); | |
}, | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Example