authorgravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-25 04:47:33+00:00
committergravatar for mlugg@mlugg.co.ukMatthew Lugg <mlugg@mlugg.co.uk> 2025-01-25 04:48:00+00:00
log5202c977d95bf65775e733264b7d37ed940a2997
tree8cbe3d64aeae8e0493ae22df84fe77bcd42facb3
parent921725427efeae591793f49291807a41112ccbf9
signaturelock-open Commit is signed but in an unrecognized format.

std: add `fs.path.fmtJoin`

This allows joining paths without allocating using a `Writer`.

1 files changed, 30 insertions(+), 1 deletions(-)

lib/std/fs/path.zig+30-1
...@@ -18,7 +18,6 @@ const debug = std.debug;...@@ -18,7 +18,6 @@ const debug = std.debug;
18const assert = debug.assert;18const assert = debug.assert;
19const testing = std.testing;19const testing = std.testing;
20const mem = std.mem;20const mem = std.mem;
21const fmt = std.fmt;
22const ascii = std.ascii;21const ascii = std.ascii;
23const Allocator = mem.Allocator;22const Allocator = mem.Allocator;
24const math = std.math;23const math = std.math;
...@@ -147,6 +146,36 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {...@@ -147,6 +146,36 @@ pub fn joinZ(allocator: Allocator, paths: []const []const u8) ![:0]u8 {
147 return out[0 .. out.len - 1 :0];146 return out[0 .. out.len - 1 :0];
148}147}
149148
149pub fn fmtJoin(paths: []const []const u8) std.fmt.Formatter(formatJoin) {
150 return .{ .data = paths };
151}
152
153fn formatJoin(paths: []const []const u8, comptime fmt: []const u8, options: std.fmt.FormatOptions, w: anytype) !void {
154 _ = fmt;
155 _ = options;
156
157 const first_path_idx = for (paths, 0..) |p, idx| {
158 if (p.len != 0) break idx;
159 } else return;
160
161 try w.writeAll(paths[first_path_idx]); // first component
162 var prev_path = paths[first_path_idx];
163 for (paths[first_path_idx + 1 ..]) |this_path| {
164 if (this_path.len == 0) continue; // skip empty components
165 const prev_sep = isSep(prev_path[prev_path.len - 1]);
166 const this_sep = isSep(this_path[0]);
167 if (!prev_sep and !this_sep) {
168 try w.writeByte(sep);
169 }
170 if (prev_sep and this_sep) {
171 try w.writeAll(this_path[1..]); // skip redundant separator
172 } else {
173 try w.writeAll(this_path);
174 }
175 prev_path = this_path;
176 }
177}
178
150fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: bool) !void {179fn testJoinMaybeZUefi(paths: []const []const u8, expected: []const u8, zero: bool) !void {
151 const uefiIsSep = struct {180 const uefiIsSep = struct {
152 fn isSep(byte: u8) bool {181 fn isSep(byte: u8) bool {