const Package = @This(); const std = @import("std"); const fs = std.fs; const mem = std.mem; const Allocator = mem.Allocator; const assert = std.debug.assert; const Compilation = @import("Compilation.zig"); const Module = @import("Module.zig"); pub const Table = std.StringHashMapUnmanaged(*Package); root_src_directory: Compilation.Directory, /// Relative to `root_src_directory`. May contain path separators. root_src_path: []const u8, table: Table = .{}, parent: ?*Package = null, /// Whether to free `root_src_directory` on `destroy`. root_src_directory_owned: bool = false, /// Allocate a Package. No references to the slices passed are kept. pub fn create( gpa: *Allocator, /// Null indicates the current working directory root_src_dir_path: ?[]const u8, /// Relative to root_src_dir_path root_src_path: []const u8, ) !*Package { const ptr = try gpa.create(Package); errdefer gpa.destroy(ptr); const owned_dir_path = if (root_src_dir_path) |p| try gpa.dupe(u8, p) else null; errdefer if (owned_dir_path) |p| gpa.free(p); const owned_src_path = try gpa.dupe(u8, root_src_path); errdefer gpa.free(owned_src_path); ptr.* = .{ .root_src_directory = .{ .path = owned_dir_path, .handle = if (owned_dir_path) |p| try fs.cwd().openDir(p, .{}) else fs.cwd(), }, .root_src_path = owned_src_path, .root_src_directory_owned = true, }; return ptr; } pub fn createWithDir( gpa: *Allocator, directory: Compilation.Directory, /// Relative to `directory`. If null, means `directory` is the root src dir /// and is owned externally. root_src_dir_path: ?[]const u8, /// Relative to root_src_dir_path root_src_path: []const u8, ) !*Package { const ptr = try gpa.create(Package); errdefer gpa.destroy(ptr); const owned_src_path = try gpa.dupe(u8, root_src_path); errdefer gpa.free(owned_src_path); if (root_src_dir_path) |p| { const owned_dir_path = try directory.join(gpa, &[1][]const u8{p}); errdefer gpa.free(owned_dir_path); ptr.* = .{ .root_src_directory = .{ .path = owned_dir_path, .handle = try directory.handle.openDir(p, .{}), }, .root_src_directory_owned = true, .root_src_path = owned_src_path, }; } else { ptr.* = .{ .root_src_directory = directory, .root_src_directory_owned = false, .root_src_path = owned_src_path, }; } return ptr; } /// Free all memory associated with this package. It does not destroy any packages /// inside its table; the caller is responsible for calling destroy() on them. pub fn destroy(pkg: *Package, gpa: *Allocator) void { gpa.free(pkg.root_src_path); if (pkg.root_src_directory_owned) { // If root_src_directory.path is null then the handle is the cwd() // which shouldn't be closed. if (pkg.root_src_directory.path) |p| { gpa.free(p); pkg.root_src_directory.handle.close(); } } { var it = pkg.table.keyIterator(); while (it.next()) |key| { gpa.free(key.*); } } pkg.table.deinit(gpa); gpa.destroy(pkg); } pub fn add(pkg: *Package, gpa: *Allocator, name: []const u8, package: *Package) !void { try pkg.table.ensureCapacity(gpa, pkg.table.count() + 1); const name_dupe = try mem.dupe(gpa, u8, name); pkg.table.putAssumeCapacityNoClobber(name_dupe, package); } pub fn addAndAdopt(parent: *Package, gpa: *Allocator, name: []const u8, child: *Package) !void { assert(child.parent == null); // make up your mind, who is the parent?? child.parent = parent; return parent.add(gpa, name, child); }