Skip to content

Commit c24d4ee

Browse files
committed
add func PathBase
1 parent 323e383 commit c24d4ee

File tree

3 files changed

+127
-0
lines changed

3 files changed

+127
-0
lines changed

README.md

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ Replace common `strings` package functions with TinyString equivalents:
8787
| `strings.TrimSuffix()` | `Convert(s).TrimSuffix(suffix).String()` |
8888
| `strings.HasPrefix()` | `HasPrefix(s, prefix)` |
8989
| `strings.HasSuffix()` | `HasSuffix(s, suffix)` |
90+
| `filepath.Base()` | `PathBase(path)` |
9091

9192
#### Other String Transformations
9293

@@ -125,6 +126,13 @@ if pos >= 0 {
125126
// Contains(s, substr) // ✅ Correct
126127
// LastIndex(s, substr) // ✅ Correct
127128

129+
130+
// PathBase
131+
PathBase("/a/b/c.txt") // -> "c.txt"
132+
PathBase("folder/file.txt") // -> "file.txt"
133+
PathBase("") // -> "."
134+
PathBase("c:\file program\app.exe") // -> "app.exe"
135+
128136
// Replace operations
129137
Convert("hello world").Replace("world", "Go").String() // out: "hello Go"
130138
Convert("test 123 test").Replace(123, 456).String() // out: "test 456 test"

filepath.go

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
package tinystring
2+
3+
// PathBase returns the last element of path, similar to
4+
// filepath.Base from the Go standard library. It treats
5+
// trailing slashes specially ("/a/b/" -> "b") and preserves
6+
// a single root slash ("/" -> "/"). An empty path returns ".".
7+
//
8+
// The implementation uses tinystring helpers (HasSuffix and Index)
9+
// to avoid importing the standard library and keep the function
10+
// minimal and TinyGo-friendly.
11+
//
12+
// Examples:
13+
//
14+
// PathBase("/a/b/c.txt") // -> "c.txt"
15+
// PathBase("folder/file.txt") // -> "file.txt"
16+
// PathBase("") // -> "."
17+
// PathBase("c:\file program\app.exe") // -> "app.exe"
18+
func PathBase(path string) string {
19+
if path == "" {
20+
return "."
21+
}
22+
23+
// prefer backslash if present
24+
sep := byte('/')
25+
if Index(path, "\\") != -1 {
26+
sep = '\\'
27+
}
28+
29+
// windows drive root like "C:\" or with only extra separators -> return "\\"
30+
if sep == '\\' && len(path) >= 2 && path[1] == ':' {
31+
onlySep := true
32+
for i := 2; i < len(path); i++ {
33+
if path[i] != '\\' && path[i] != '/' {
34+
onlySep = false
35+
break
36+
}
37+
}
38+
if onlySep {
39+
return "\\"
40+
}
41+
}
42+
43+
// trim trailing separators
44+
for len(path) > 1 && path[len(path)-1] == sep {
45+
path = path[:len(path)-1]
46+
}
47+
48+
// if path reduced to a single root separator, return it
49+
if len(path) == 1 && (path[0] == '/' || path[0] == '\\') {
50+
return path
51+
}
52+
53+
// search from end for last separator
54+
for i := len(path) - 1; i >= 0; i-- {
55+
if path[i] == sep {
56+
return path[i+1:]
57+
}
58+
}
59+
return path
60+
}

filepath_test.go

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
package tinystring
2+
3+
import "testing"
4+
5+
func TestFilePathBase(t *testing.T) {
6+
tests := []struct {
7+
path string
8+
want string
9+
}{
10+
{"", "."},
11+
{"/", "/"},
12+
{"///", "/"},
13+
{"a", "a"},
14+
{"a/b", "b"},
15+
{"a/b/", "b"},
16+
{"/a/b/c", "c"},
17+
{"/a/b/c/", "c"},
18+
{"///a", "a"},
19+
{"a//b", "b"},
20+
{".", "."},
21+
{"..", ".."},
22+
{"no/slash_at_end", "slash_at_end"},
23+
// file extension cases
24+
{"file.txt", "file.txt"},
25+
{"dir/file.txt", "file.txt"},
26+
{"/path/to/archive.tar.gz", "archive.tar.gz"},
27+
{"dir/.hidden", ".hidden"},
28+
{"a/b.c/d.e", "d.e"},
29+
{"/trailing/.bashrc/", ".bashrc"},
30+
}
31+
32+
for _, tc := range tests {
33+
t.Run(tc.path, func(t *testing.T) {
34+
got := PathBase(tc.path)
35+
if got != tc.want {
36+
t.Fatalf("FilePathBase(%q) = %q; want %q", tc.path, got, tc.want)
37+
}
38+
})
39+
}
40+
}
41+
42+
func TestPathBaseWindows(t *testing.T) {
43+
tests := []struct{ path, want string }{
44+
{`C:\`, `\`},
45+
{`C:\file.exe`, `file.exe`},
46+
{`C:\Program Files\App\app.exe`, `app.exe`},
47+
{`C:\dir\\sub\file.txt`, `file.txt`},
48+
{`\\server\share\file`, `file`},
49+
}
50+
51+
for _, tc := range tests {
52+
t.Run(tc.path, func(t *testing.T) {
53+
got := PathBase(tc.path)
54+
if got != tc.want {
55+
t.Fatalf("PathBase(%q) = %q; want %q", tc.path, got, tc.want)
56+
}
57+
})
58+
}
59+
}

0 commit comments

Comments
 (0)