1const std = @import("std");
2const gpa = std.heap.c_allocator;
3const string = []const u8;
4const style = @import("ansi").style;
5const licenses = @import("licenses");
6const extras = @import("extras");
7const nfs = @import("nfs");
8
9const zigmod = @import("../lib.zig");
10const u = @import("./../util/funcs.zig");
11const common = @import("./../common.zig");
12
13const List = std.array_list.Managed(zigmod.Module);
14const Map = std.array_hash_map.String(*List);
15
16// Inspired by:
17// https://github.com/onur/cargo-license
18
19pub fn execute(self_name: []const u8, args: []const [:0]const u8) !void {
20 _ = self_name;
21 _ = args;
22
23 const cachepath = try u.find_cachepath();
24 const dir = nfs.cwd();
25
26 var options = common.CollectOptions{
27 .log = false,
28 .update = false,
29 .alloc = gpa,
30 };
31
32 try do(cachepath, dir, &options, .stdout());
33}
34
35pub fn do(cachepath: [:0]const u8, dir: nfs.Dir, options: *common.CollectOptions, outfile: nfs.File) !void {
36 const top_module = try common.collect_deps_deep(cachepath, dir, options);
37
38 var master_list = List.init(gpa);
39 errdefer master_list.deinit();
40 try common.collect_pkgs(top_module, &master_list);
41 std.mem.sort(zigmod.Module, master_list.items, {}, zigmod.Module.lessThan);
42
43 var map = Map{};
44 errdefer map.deinit(gpa);
45
46 var unspecified_list = List.init(gpa);
47 errdefer unspecified_list.deinit();
48
49 for (master_list.items) |item| {
50 if (item.clean_path.len == 0) {
51 continue;
52 }
53 if (std.mem.eql(u8, item.clean_path, "files")) {
54 continue;
55 }
56 if (item.yaml == null) {
57 try unspecified_list.append(item);
58 continue;
59 }
60 const license_code = item.yaml.?.get_string("license") orelse "";
61 if (license_code.len == 0) {
62 try unspecified_list.append(item);
63 continue;
64 }
65 const map_item = try map.getOrPut(gpa, license_code);
66 if (!map_item.found_existing) {
67 const temp = try gpa.create(List);
68 temp.* = List.init(gpa);
69 map_item.value_ptr.* = temp;
70 }
71 const tracking_list = map_item.value_ptr.*;
72 try tracking_list.append(item);
73 }
74
75 const istty = outfile.isatty();
76 const writer = outfile;
77 const Bold = if (!istty) "" else style.Bold;
78 const Faint = if (!istty) "" else style.Faint;
79 const ResetIntensity = if (!istty) "" else style.ResetIntensity;
80
81 var first = true;
82 var iter = map.iterator();
83 while (iter.next()) |entry| {
84 if (!first) try writer.writeAll("\n");
85 first = false;
86 try writer.writeAll(Bold);
87 try writer.print("{s}:\n", .{entry.key_ptr.*});
88 if (extras.containsString(licenses.spdx, entry.key_ptr.*)) {
89 try writer.writeAll(Faint);
90 try writer.print("= {s}{s}\n", .{ "https://spdx.org/licenses/", entry.key_ptr.* });
91 }
92 try writer.writeAll(ResetIntensity);
93 for (entry.value_ptr.*.items) |item| {
94 if (std.mem.eql(u8, item.clean_path, "../..")) {
95 try writer.writeAll("- This\n");
96 } else {
97 try writer.print("- {s} {s}\n", .{ @tagName(item.dep.?.type), item.dep.?.path });
98 }
99 }
100 }
101 if (unspecified_list.items.len > 0) {
102 if (!first) try writer.writeAll("\n");
103 try writer.writeAll(Bold);
104 try writer.writeAll("Unspecified:\n");
105 try writer.writeAll(ResetIntensity);
106 for (unspecified_list.items) |item| {
107 if (std.mem.eql(u8, item.clean_path, "../..")) {
108 try writer.writeAll("- This\n");
109 } else {
110 try writer.print("- {s} {s}\n", .{ @tagName(item.dep.?.type), item.dep.?.path });
111 }
112 }
113 }
114}