High-level wrapper around Flecs, a powerful ECS (Entity Component System) library, written in Zig language
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

133 lines
4.9 KiB

const std = @import("std");
const Allocator = std.mem.Allocator;
const c = @import("./c.zig");
pub var default_seperator = "/";
pub var default_prefix = "/";
// TODO: Do something better than just `std.debug.assert`.
pub const Path = struct {
/// Whether the path is specified to be absolute.
/// Note that a relative path can still be interpreted as absolute.
absolute: bool,
/// The string parts that make up the path.
parts: []const []const u8,
/// Specifies how the path has been allocated.
/// Defines what happens when `deinit()` is called.
alloc: union(enum) {
/// None of the data is owned by the path.
none,
/// The parts array itself is owned by the path.
array: Allocator,
/// The parts array and the contained parts are owned by the path.
parts: Allocator,
},
/// Creates a new `Path` from the specified string parts.
/// The resulting path does not own any of the given slices.
pub fn fromParts(absolute: bool, parts: []const []const u8) !Path {
std.debug.assert(parts.len > 0);
return .{ .absolute = absolute, .parts = parts, .alloc = .none };
}
/// Creates a new `Path` from the specified string using the default
/// seperator and prefix strings.
///
/// If the string starts with the default prefix (if any), the resulting
/// path will be absolute. The rest of the string will be split by the
/// default seperator, becoming its parts.
///
/// The parts array will be allocated with the specified allocator and is
/// owned by the resulting path. `deinit()` must be called to free it.
pub fn fromString(path: []const u8, alloc: Allocator) !Path {
return fromStringCustom(path, default_seperator, default_prefix, alloc);
}
/// Creates a new `Path` from the specified string using the specified
/// seperator and optionally a prefix strings.
///
/// If the string starts with the specified prefix (if any), the resulting
/// path will be absolute. The rest of the string will be split by the
/// specified seperator, becoming its parts.
///
/// The parts array will be allocated with the specified allocator and is
/// owned by the resulting path. `deinit()` must be called to free it.
pub fn fromStringCustom(path: []const u8, sep: []const u8, prefix: ?[]const u8, alloc: Allocator) !Path {
std.debug.assert(path.len > 0);
var remaining = path;
var absolute = false;
// If prefix is defined and path starts with it, the path is absolute.
if (prefix) |p| {
if (std.mem.startsWith(u8, remaining, p)) {
remaining = remaining[p.len..];
absolute = true;
}
}
const parts_len = std.mem.count(u8, remaining, sep) + 1;
const parts = try alloc.alloc([]const u8, parts_len);
var i: usize = 0;
var it = std.mem.splitSequence(u8, remaining, sep);
while (it.next()) |part| : (i += 1) parts[i] = part;
return .{ .absolute = absolute, .parts = parts, .alloc = .{ .array = alloc } };
}
pub fn clone(self: Path) !Path {
_ = self;
return error.NotImplemented;
}
pub fn deinit(self: Path) void {
switch (self.alloc) {
.none => {}, // Do nothing.
.array => |alloc| alloc.free(self.parts),
.parts => |alloc| {
for (self.parts) |p| alloc.free(p);
alloc.free(self.parts);
},
}
}
};
test Path {
const alloc = std.testing.allocator;
// const expect = std.testing.expect;
const expectEql = std.testing.expectEqual;
const expectStrEql = std.testing.expectEqualStrings;
const path1 = try Path.fromStringCustom("some/relative/path", "/", "/", alloc);
defer path1.deinit();
try expectEql(false, path1.absolute);
try expectEql(@as(usize, 3), path1.parts.len);
try expectStrEql("some", path1.parts[0]);
try expectStrEql("relative", path1.parts[1]);
try expectStrEql("path", path1.parts[2]);
const path2 = try Path.fromStringCustom("/absolute", "/", "/", alloc);
defer path2.deinit();
try expectEql(true, path2.absolute);
try expectEql(@as(usize, 1), path2.parts.len);
try expectStrEql("absolute", path2.parts[0]);
const path3 = try Path.fromStringCustom("foo.bar.baz", ".", null, alloc);
defer path3.deinit();
try expectEql(false, path3.absolute);
try expectEql(@as(usize, 3), path3.parts.len);
try expectPartsEql(&.{ "foo", "bar", "baz" }, path3.parts);
const path4 = try Path.fromParts(true, &.{ "I", "am", "Groot!" });
// defer path4.deinit(); -- Not necessary, cannot leak.
try expectEql(true, path4.absolute);
try expectPartsEql(&.{ "I", "am", "Groot!" }, path4.parts);
}
fn expectPartsEql(expected: []const []const u8, actual: []const []const u8) !void {
try std.testing.expectEqualDeep(expected, actual);
}