From 25542738058de6361f71e5a39a7c64463956eaa0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Fri, 13 Nov 2020 11:53:51 +0100 Subject: [PATCH 1/5] Implements std.fs.path.extension. --- lib/std/fs/path.zig | 57 +++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 57 insertions(+) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 5e418e8e5c..8317b2d6a0 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -1182,3 +1182,60 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons defer testing.allocator.free(result); testing.expectEqualSlices(u8, expected_output, result); } + +/// Returns the extension of the file name (if any). +/// This function will search for the file extension (separated by a `.`) and will return the text after the `.`. +/// Files that end with `.` are considered to have no extension. +pub fn extension(path: []const u8) ?[]const u8 { + const filename = basename(path); + return if (std.mem.lastIndexOf(u8, filename, ".")) |index| + if (index == filename.len - 1) + null + else + filename[index + 1 ..] + else + null; +} + +fn testExtension(path: []const u8, expected: ?[]const u8) void { + const actual = extension(path); + + if (expected) |must_be| { + std.testing.expect(actual != null); + std.testing.expectEqualStrings(must_be, actual.?); + } else { + std.testing.expectEqual(expected, actual); + } +} + +test "extension" { + testExtension("", null); + testExtension(".", null); + testExtension("a.", null); + testExtension("abc.", null); + testExtension(".a", "a"); + testExtension(".file", "file"); + testExtension(".gitignore", "gitignore"); + testExtension("file.gitignore", "gitignore"); + testExtension("a.gitignore", "gitignore"); + + testExtension("/", null); + testExtension("/.", null); + testExtension("/a.", null); + testExtension("/abc.", null); + testExtension("/.a", "a"); + testExtension("/.file", "file"); + testExtension("/.gitignore", "gitignore"); + testExtension("/file.gitignore", "gitignore"); + testExtension("/a.gitignore", "gitignore"); + + testExtension("/foo/bar/bam/", null); + testExtension("/foo/bar/bam/.", null); + testExtension("/foo/bar/bam/a.", null); + testExtension("/foo/bar/bam/abc.", null); + testExtension("/foo/bar/bam/.a", "a"); + testExtension("/foo/bar/bam/.file", "file"); + testExtension("/foo/bar/bam/.gitignore", "gitignore"); + testExtension("/foo/bar/bam/file.gitignore", "gitignore"); + testExtension("/foo/bar/bam/a.gitignore", "gitignore"); +} From f51064f79ec4c240251686e73eb33d1a9984a88c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Fri, 13 Nov 2020 12:48:01 +0100 Subject: [PATCH 2/5] Respects leading `.` and ignores it. --- lib/std/fs/path.zig | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index 8317b2d6a0..e21991e4ee 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -1185,11 +1185,17 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons /// Returns the extension of the file name (if any). /// This function will search for the file extension (separated by a `.`) and will return the text after the `.`. -/// Files that end with `.` are considered to have no extension. +/// Files that end with `.` are considered to have no extension, files that start with `.` +/// Examples: +/// - `"main.zig"` ⇒ `"zig"` +/// - `"src/main.zig"` ⇒ `"zig"` +/// - `".gitignore"` ⇒ `null` +/// - `"keep."` ⇒ `null` +/// - `"src.keep.me"` ⇒ `"me"` pub fn extension(path: []const u8) ?[]const u8 { const filename = basename(path); return if (std.mem.lastIndexOf(u8, filename, ".")) |index| - if (index == filename.len - 1) + if (index == 0 or index == filename.len - 1) null else filename[index + 1 ..] @@ -1213,29 +1219,32 @@ test "extension" { testExtension(".", null); testExtension("a.", null); testExtension("abc.", null); - testExtension(".a", "a"); - testExtension(".file", "file"); - testExtension(".gitignore", "gitignore"); + testExtension(".a", null); + testExtension(".file", null); + testExtension(".gitignore", null); testExtension("file.gitignore", "gitignore"); testExtension("a.gitignore", "gitignore"); + testExtension("a.b.c", "c"); testExtension("/", null); testExtension("/.", null); testExtension("/a.", null); testExtension("/abc.", null); - testExtension("/.a", "a"); - testExtension("/.file", "file"); - testExtension("/.gitignore", "gitignore"); + testExtension("/.a", null); + testExtension("/.file", null); + testExtension("/.gitignore", null); testExtension("/file.gitignore", "gitignore"); testExtension("/a.gitignore", "gitignore"); + testExtension("/a.b.c", "c"); testExtension("/foo/bar/bam/", null); testExtension("/foo/bar/bam/.", null); testExtension("/foo/bar/bam/a.", null); testExtension("/foo/bar/bam/abc.", null); - testExtension("/foo/bar/bam/.a", "a"); - testExtension("/foo/bar/bam/.file", "file"); - testExtension("/foo/bar/bam/.gitignore", "gitignore"); + testExtension("/foo/bar/bam/.a", null); + testExtension("/foo/bar/bam/.file", null); + testExtension("/foo/bar/bam/.gitignore", null); testExtension("/foo/bar/bam/file.gitignore", "gitignore"); testExtension("/foo/bar/bam/a.gitignore", "gitignore"); + testExtension("/foo/bar/bam/a.b.c", "c"); } From 2ced0316ebff1227ffe8e08b5d395754be10ef13 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Fri, 13 Nov 2020 13:54:41 +0100 Subject: [PATCH 3/5] Makes @haze happy. --- lib/std/fs/path.zig | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index e21991e4ee..c683b2b78c 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -1222,8 +1222,8 @@ test "extension" { testExtension(".a", null); testExtension(".file", null); testExtension(".gitignore", null); - testExtension("file.gitignore", "gitignore"); - testExtension("a.gitignore", "gitignore"); + testExtension("file.ext", "ext"); + testExtension("very-long-file.bruh", "bruh"); testExtension("a.b.c", "c"); testExtension("/", null); @@ -1233,8 +1233,8 @@ test "extension" { testExtension("/.a", null); testExtension("/.file", null); testExtension("/.gitignore", null); - testExtension("/file.gitignore", "gitignore"); - testExtension("/a.gitignore", "gitignore"); + testExtension("/file.ext", "ext"); + testExtension("/very-long-file.bruh", "bruh"); testExtension("/a.b.c", "c"); testExtension("/foo/bar/bam/", null); @@ -1244,7 +1244,7 @@ test "extension" { testExtension("/foo/bar/bam/.a", null); testExtension("/foo/bar/bam/.file", null); testExtension("/foo/bar/bam/.gitignore", null); - testExtension("/foo/bar/bam/file.gitignore", "gitignore"); - testExtension("/foo/bar/bam/a.gitignore", "gitignore"); + testExtension("/foo/bar/bam/file.ext", "ext"); + testExtension("/foo/bar/bam/very-long-file.bruh", "bruh"); testExtension("/foo/bar/bam/a.b.c", "c"); } From 0bbf170514c74a70cf7fa5a9c044335a8f9dddd4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Sat, 14 Nov 2020 11:01:02 +0100 Subject: [PATCH 4/5] =?UTF-8?q?Adapts=20to=20@andrewrk=E2=80=8Bs=20comment?= =?UTF-8?q?=20to=20include=20dot.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- lib/std/fs/path.zig | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index c683b2b78c..b053b3e4ec 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -1187,18 +1187,18 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons /// This function will search for the file extension (separated by a `.`) and will return the text after the `.`. /// Files that end with `.` are considered to have no extension, files that start with `.` /// Examples: -/// - `"main.zig"` ⇒ `"zig"` -/// - `"src/main.zig"` ⇒ `"zig"` +/// - `"main.zig"` ⇒ `".zig"` +/// - `"src/main.zig"` ⇒ `".zig"` /// - `".gitignore"` ⇒ `null` /// - `"keep."` ⇒ `null` -/// - `"src.keep.me"` ⇒ `"me"` +/// - `"src.keep.me"` ⇒ `".me"` pub fn extension(path: []const u8) ?[]const u8 { const filename = basename(path); return if (std.mem.lastIndexOf(u8, filename, ".")) |index| if (index == 0 or index == filename.len - 1) null else - filename[index + 1 ..] + filename[index..] else null; } @@ -1222,9 +1222,9 @@ test "extension" { testExtension(".a", null); testExtension(".file", null); testExtension(".gitignore", null); - testExtension("file.ext", "ext"); - testExtension("very-long-file.bruh", "bruh"); - testExtension("a.b.c", "c"); + testExtension("file.ext", ".ext"); + testExtension("very-long-file.bruh", ".bruh"); + testExtension("a.b.c", ".c"); testExtension("/", null); testExtension("/.", null); @@ -1233,9 +1233,9 @@ test "extension" { testExtension("/.a", null); testExtension("/.file", null); testExtension("/.gitignore", null); - testExtension("/file.ext", "ext"); - testExtension("/very-long-file.bruh", "bruh"); - testExtension("/a.b.c", "c"); + testExtension("/file.ext", ".ext"); + testExtension("/very-long-file.bruh", ".bruh"); + testExtension("/a.b.c", ".c"); testExtension("/foo/bar/bam/", null); testExtension("/foo/bar/bam/.", null); @@ -1244,7 +1244,7 @@ test "extension" { testExtension("/foo/bar/bam/.a", null); testExtension("/foo/bar/bam/.file", null); testExtension("/foo/bar/bam/.gitignore", null); - testExtension("/foo/bar/bam/file.ext", "ext"); - testExtension("/foo/bar/bam/very-long-file.bruh", "bruh"); - testExtension("/foo/bar/bam/a.b.c", "c"); + testExtension("/foo/bar/bam/file.ext", ".ext"); + testExtension("/foo/bar/bam/very-long-file.bruh", ".bruh"); + testExtension("/foo/bar/bam/a.b.c", ".c"); } From b3af2f68b38c542f7d8bb5b1292c5301a07f9cac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20=28xq=29=20Quei=C3=9Fner?= Date: Wed, 18 Nov 2020 00:03:24 +0100 Subject: [PATCH 5/5] Changes behaviour from std.fs.path.extension from returning `null` to returning `""`. --- lib/std/fs/path.zig | 72 +++++++++++++++++++++++---------------------- 1 file changed, 37 insertions(+), 35 deletions(-) diff --git a/lib/std/fs/path.zig b/lib/std/fs/path.zig index b053b3e4ec..5016a72652 100644 --- a/lib/std/fs/path.zig +++ b/lib/std/fs/path.zig @@ -28,6 +28,7 @@ pub const delimiter_windows = ';'; pub const delimiter_posix = ':'; pub const delimiter = if (builtin.os.tag == .windows) delimiter_windows else delimiter_posix; +/// Returns if the given byte is a valid path separator pub fn isSep(byte: u8) bool { if (builtin.os.tag == .windows) { return byte == '/' or byte == '\\'; @@ -1189,62 +1190,63 @@ fn testRelativeWindows(from: []const u8, to: []const u8, expected_output: []cons /// Examples: /// - `"main.zig"` ⇒ `".zig"` /// - `"src/main.zig"` ⇒ `".zig"` -/// - `".gitignore"` ⇒ `null` -/// - `"keep."` ⇒ `null` +/// - `".gitignore"` ⇒ `""` +/// - `"keep."` ⇒ `""` /// - `"src.keep.me"` ⇒ `".me"` -pub fn extension(path: []const u8) ?[]const u8 { +/// - `"/src/keep.me"` ⇒ `".me"` +/// - `"/src/keep.me/"` ⇒ `".me"` +pub fn extension(path: []const u8) []const u8 { const filename = basename(path); return if (std.mem.lastIndexOf(u8, filename, ".")) |index| if (index == 0 or index == filename.len - 1) - null + "" else filename[index..] else - null; + ""; } -fn testExtension(path: []const u8, expected: ?[]const u8) void { - const actual = extension(path); - - if (expected) |must_be| { - std.testing.expect(actual != null); - std.testing.expectEqualStrings(must_be, actual.?); - } else { - std.testing.expectEqual(expected, actual); - } +fn testExtension(path: []const u8, expected: []const u8) void { + std.testing.expectEqualStrings(expected, extension(path)); } test "extension" { - testExtension("", null); - testExtension(".", null); - testExtension("a.", null); - testExtension("abc.", null); - testExtension(".a", null); - testExtension(".file", null); - testExtension(".gitignore", null); + testExtension("", ""); + testExtension(".", ""); + testExtension("a.", ""); + testExtension("abc.", ""); + testExtension(".a", ""); + testExtension(".file", ""); + testExtension(".gitignore", ""); testExtension("file.ext", ".ext"); + testExtension("file.ext.", ""); testExtension("very-long-file.bruh", ".bruh"); testExtension("a.b.c", ".c"); + testExtension("a.b.c/", ".c"); - testExtension("/", null); - testExtension("/.", null); - testExtension("/a.", null); - testExtension("/abc.", null); - testExtension("/.a", null); - testExtension("/.file", null); - testExtension("/.gitignore", null); + testExtension("/", ""); + testExtension("/.", ""); + testExtension("/a.", ""); + testExtension("/abc.", ""); + testExtension("/.a", ""); + testExtension("/.file", ""); + testExtension("/.gitignore", ""); testExtension("/file.ext", ".ext"); + testExtension("/file.ext.", ""); testExtension("/very-long-file.bruh", ".bruh"); testExtension("/a.b.c", ".c"); + testExtension("/a.b.c/", ".c"); - testExtension("/foo/bar/bam/", null); - testExtension("/foo/bar/bam/.", null); - testExtension("/foo/bar/bam/a.", null); - testExtension("/foo/bar/bam/abc.", null); - testExtension("/foo/bar/bam/.a", null); - testExtension("/foo/bar/bam/.file", null); - testExtension("/foo/bar/bam/.gitignore", null); + testExtension("/foo/bar/bam/", ""); + testExtension("/foo/bar/bam/.", ""); + testExtension("/foo/bar/bam/a.", ""); + testExtension("/foo/bar/bam/abc.", ""); + testExtension("/foo/bar/bam/.a", ""); + testExtension("/foo/bar/bam/.file", ""); + testExtension("/foo/bar/bam/.gitignore", ""); testExtension("/foo/bar/bam/file.ext", ".ext"); + testExtension("/foo/bar/bam/file.ext.", ""); testExtension("/foo/bar/bam/very-long-file.bruh", ".bruh"); testExtension("/foo/bar/bam/a.b.c", ".c"); + testExtension("/foo/bar/bam/a.b.c/", ".c"); }