diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f694dbbc7d37e91e8c57319d51d10b9417d13a25..6a1ce19e9c870e40521eeca9c528d77741f49c02 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -59,6 +59,10 @@ jobs: - run: ./test_repo.sh https://github.com/nektro/zigmod-test-local-dep - run: ./test_repo.sh https://github.com/nektro/zigmod-test-c-code + - run: git diff <(cat __snapshots__/explain.tree.txt) <(zigmod explain --locked --format tree) + - run: git diff <(cat __snapshots__/explain.mermaid.txt) <(zigmod explain --locked --format mermaid) + - run: git diff <(cat __snapshots__/explain.dot.txt) <(zigmod explain --locked --format dot) + # Github Release - run: ./changelog.sh - run: ./make_release.sh ${{ secrets.GITHUB_TOKEN }} diff --git a/__snapshots__/explain.dot.txt b/__snapshots__/explain.dot.txt new file mode 100644 index 0000000000000000000000000000000000000000..e987344a7f47f010258d762bf1c8268b2c2a20f5 --- /dev/null +++ b/__snapshots__/explain.dot.txt @@ -0,0 +1,33 @@ +digraph { + "root" -> "zigmod"; + "root" -> "win32"; + "root" -> "extras"; + "root" -> "ansi"; + "zigmod" -> "yaml"; + "zigmod" -> "ansi"; + "zigmod" -> "known-folders"; + "zigmod" -> "licenses"; + "zigmod" -> "zfetch"; + "zigmod" -> "detect-license"; + "zigmod" -> "inquirer"; + "zigmod" -> "ini"; + "zigmod" -> "time"; + "zigmod" -> "extras"; + "zigmod" -> "git"; + "zigmod" -> "json"; + "yaml" -> "extras"; + "zfetch" -> "hzzp"; + "zfetch" -> "iguanaTLS"; + "detect-license" -> "licenses-text"; + "detect-license" -> "leven"; + "inquirer" -> "ansi"; + "time" -> "extras"; + "git" -> "time"; + "git" -> "extras"; + "git" -> "tracer"; + "tracer" -> "extras"; + "json" -> "extras"; + "json" -> "tracer"; + "json" -> "intrusive-parser"; + "intrusive-parser" -> "extras"; +} diff --git a/__snapshots__/explain.mermaid.txt b/__snapshots__/explain.mermaid.txt new file mode 100644 index 0000000000000000000000000000000000000000..3d535bb27f68f0526e497ff8715d0fa2143e0ce3 --- /dev/null +++ b/__snapshots__/explain.mermaid.txt @@ -0,0 +1,32 @@ +graph TD; + root-->zigmod; + root-->win32; + root-->extras; + root-->ansi; + zigmod-->yaml; + zigmod-->ansi; + zigmod-->known-folders; + zigmod-->licenses; + zigmod-->zfetch; + zigmod-->detect-license; + zigmod-->inquirer; + zigmod-->ini; + zigmod-->time; + zigmod-->extras; + zigmod-->git; + zigmod-->json; + yaml-->extras; + zfetch-->hzzp; + zfetch-->iguanaTLS; + detect-license-->licenses-text; + detect-license-->leven; + inquirer-->ansi; + time-->extras; + git-->time; + git-->extras; + git-->tracer; + tracer-->extras; + json-->extras; + json-->tracer; + json-->intrusive-parser; + intrusive-parser-->extras; diff --git a/__snapshots__/explain.tree.txt b/__snapshots__/explain.tree.txt new file mode 100644 index 0000000000000000000000000000000000000000..1fa7aa87d05c2e06aeaacc2fec04bc06750b535b --- /dev/null +++ b/__snapshots__/explain.tree.txt @@ -0,0 +1,35 @@ +root + zigmod + yaml + + extras + ansi + known-folders + licenses + zfetch + hzzp + iguanaTLS + detect-license + licenses-text + leven + inquirer + ansi + ini + time + extras + extras + git + time + extras + extras + tracer + extras + json + extras + tracer + extras + intrusive-parser + extras + win32 + extras + ansi diff --git a/docs/commands/explain.md b/docs/commands/explain.md new file mode 100644 index 0000000000000000000000000000000000000000..20b65727755bdbfb0ce28fe58f5f3b9dcd684e43 --- /dev/null +++ b/docs/commands/explain.md @@ -0,0 +1,37 @@ +## `explain` command + +``` +zigmod explain +``` + +Use this command to create a visual description of your dependency graph. + +## `--locked` + +Pass this to use the dependency versions from `zigmod.lock` instead of your current `.zigmod` folder. + +## `--format` + +May be followed by one of the following values: + +- `tree` +- `mermaid` +- `dot` + +## `--format tree` + +Image + +## `--format mermaid` + +https://docs.github.com/en/get-started/writing-on-github/working-with-advanced-formatting/creating-diagrams + +Image + +## `--format dot` + +This format prints output compatible with the `dot` program from https://graphviz.org/. + +Run `dot -Tpng <(zigmod explain --locked --format dot) -o explain.png` to generate the image. + +Image diff --git a/src/cmd/explain.zig b/src/cmd/explain.zig new file mode 100644 index 0000000000000000000000000000000000000000..25f119dfb36decbfbcb6d26527ce30e4da9418ed --- /dev/null +++ b/src/cmd/explain.zig @@ -0,0 +1,100 @@ +const std = @import("std"); +const builtin = @import("builtin"); +const extras = @import("extras"); + +const zigmod = @import("../lib.zig"); +const u = @import("./../util/funcs.zig"); +const common = @import("./../common.zig"); + +// +// + +pub fn execute(self_name: []const u8, args: [][:0]u8) !void { + _ = self_name; + + const gpa = std.heap.c_allocator; + const cachepath = try u.find_cachepath(); + const dir = std.fs.cwd(); + const should_lock = args.len >= 1 and std.mem.eql(u8, args[0], "--locked"); + const format_i: usize = if (should_lock) 1 else 0; + const Format = enum { + tree, + mermaid, + dot, + }; + const format_s = if (args.len >= 1 + format_i and std.mem.eql(u8, args[format_i], "--format")) args[format_i + 1] else ""; + const format = std.meta.stringToEnum(Format, format_s) orelse u.fail("unrecognized --format: {s}", .{format_s}); + + var options = common.CollectOptions{ + .log = false, + .update = false, + .alloc = gpa, + .lock = if (should_lock) try common.parse_lockfile(gpa, dir) else null, + }; + const top_module = try common.collect_deps_deep(cachepath, dir, &options); + + var seencache = std.ArrayList([48]u8).init(gpa); + defer seencache.deinit(); + + const stdout = std.io.getStdOut(); + const w = stdout.writer(); + + switch (format) { + .tree => { + try printTree(w, top_module, 0); + }, + .mermaid => { + try w.writeAll("graph TD;\n"); + try printMermaid(w, top_module, &seencache); + }, + .dot => { + try w.writeAll("digraph {\n"); + try printDot(w, top_module, &seencache); + try w.writeAll("}\n"); + }, + } +} + +fn printTree(writer: anytype, module: zigmod.Module, depth: u16) !void { + try writer.writeByteNTimes('\t', depth); + try writer.writeAll(module.name); + try writer.writeByte('\n'); + + for (module.deps) |dep| { + try printTree(writer, dep, depth + 1); + } +} + +fn printMermaid(writer: anytype, module: zigmod.Module, seencache: *std.ArrayList([48]u8)) !void { + for (seencache.items) |item| { + if (std.mem.eql(u8, &module.id, &item)) { + return; + } + } + try seencache.append(module.id); + + for (module.deps) |dep| { + if (dep.name.len == 0) continue; + try writer.print(" {s}-->{s};\n", .{ module.name, dep.name }); + } + for (module.deps) |dep| { + try printMermaid(writer, dep, seencache); + } +} + +fn printDot(writer: anytype, module: zigmod.Module, seencache: *std.ArrayList([48]u8)) !void { + for (seencache.items) |item| { + if (std.mem.eql(u8, &module.id, &item)) { + return; + } + } + try seencache.append(module.id); + + for (module.deps) |dep| { + if (dep.name.len == 0) continue; + try writer.print(" \"{s}\" -> \"{s}\";\n", .{ module.name, dep.name }); + } + for (module.deps) |dep| { + try printDot(writer, dep, seencache); + } +} diff --git a/src/lib.zig b/src/lib.zig index 241038e3baa31eb9f0153422c22f7b7cbb7b666c..8116ca41ee469c9f7f10346d3d7016088e5f2c40 100644 --- a/src/lib.zig +++ b/src/lib.zig @@ -13,6 +13,7 @@ pub const commands = struct { pub const zpm = @import("./cmd/zpm.zig"); pub const license = @import("./cmd/license.zig"); pub const generate = @import("./cmd/generate.zig"); + pub const explain = @import("./cmd/explain.zig"); }; pub fn init() !void {