Skip to content

Commit 323e383

Browse files
committed
add funcs HasPrefix, HasSuffix with test and docs
1 parent b7fd945 commit 323e383

File tree

3 files changed

+108
-1
lines changed

3 files changed

+108
-1
lines changed

README.md

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@ Replace common `strings` package functions with TinyString equivalents:
8585
| `strings.TrimSpace()` | `Convert(s).TrimSpace().String()` |
8686
| `strings.TrimPrefix()` | `Convert(s).TrimPrefix(prefix).String()` |
8787
| `strings.TrimSuffix()` | `Convert(s).TrimSuffix(suffix).String()` |
88+
| `strings.HasPrefix()` | `HasPrefix(s, prefix)` |
89+
| `strings.HasSuffix()` | `HasSuffix(s, suffix)` |
8890

8991
#### Other String Transformations
9092

@@ -103,6 +105,14 @@ pos := Index("hello world", "world") // out: 6 (first occurrenc
103105
found := Contains("hello world", "world") // out: true
104106
count := Count("abracadabra", "abra") // out: 2
105107

108+
// Prefix / Suffix checks
109+
isPref := HasPrefix("hello", "he") // out: true
110+
isSuf := HasSuffix("file.txt", ".txt") // out: true
111+
112+
// Note: this library follows the standard library semantics for prefixes/suffixes:
113+
// an empty prefix or suffix is considered a match (HasPrefix(s, "") == true,
114+
// HasSuffix(s, "") == true).
115+
106116
// Find last occurrence (useful for file extensions)
107117
pos := LastIndex("image.backup.jpg", ".") // out: 12
108118
if pos >= 0 {

search.go

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ package tinystring
1313
func Index(s, substr string) int {
1414
n := len(substr)
1515
if n == 0 {
16-
return 0 // Comportamiento estándar: cadena vacía se encuentra en posición 0
16+
return 0 // Standard behavior: empty string is found at position 0
1717
}
1818
if n == 1 {
1919
// Optimized single byte search
@@ -80,3 +80,43 @@ func Contains(Conv, search string) bool {
8080
}
8181
return Index(Conv, search) != -1
8282
}
83+
84+
// HasPrefix reports whether the string 'conv' begins with 'prefix'.
85+
// Implemented using Index for consistency with other helpers in this package.
86+
//
87+
// Examples:
88+
//
89+
// HasPrefix("hello", "he") // returns true
90+
// HasPrefix("hello", "hello") // returns true
91+
// HasPrefix("hello", "") // returns false (empty prefix)
92+
// HasPrefix("a", "abc") // returns false (prefix longer than string)
93+
func HasPrefix(conv, prefix string) bool {
94+
if len(prefix) == 0 {
95+
return true // Follow stdlib semantics: empty string is a prefix of any string
96+
}
97+
if len(conv) < len(prefix) {
98+
return false
99+
}
100+
// Search only in the slice that should match the prefix; Index must return 0.
101+
return Index(conv[:len(prefix)], prefix) == 0
102+
}
103+
104+
// HasSuffix reports whether the string 'Conv' ends with 'suffix'.
105+
// Implemented using Index by checking the tail of the string.
106+
//
107+
// Examples:
108+
//
109+
// HasSuffix("testing", "ing") // returns true
110+
// HasSuffix("file.txt", ".txt") // returns true
111+
// HasSuffix("hello", "") // returns false (empty suffix)
112+
// HasSuffix("go", "golang") // returns false
113+
func HasSuffix(conv, suffix string) bool {
114+
if len(suffix) == 0 {
115+
return true // Follow stdlib semantics: empty string is a suffix of any string
116+
}
117+
if len(conv) < len(suffix) {
118+
return false
119+
}
120+
// The suffix should be at the start of the tail slice
121+
return Index(conv[len(conv)-len(suffix):], suffix) == 0
122+
}

searchHasPrefixSuffix_test.go

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,57 @@
1+
package tinystring
2+
3+
import "testing"
4+
5+
func TestHasPrefix(t *testing.T) {
6+
tests := []struct {
7+
name string
8+
s string
9+
prefix string
10+
want bool
11+
}{
12+
{"empty prefix", "hello", "", true},
13+
{"exact match", "hello", "hello", true},
14+
{"short prefix", "hello", "he", true},
15+
{"not a prefix", "hello", "ello", false},
16+
{"prefix longer than string", "hi", "hello", false},
17+
{"single byte", "abc", "a", true},
18+
{"null byte prefix", "a\x00b", "a\x00", true},
19+
{"unicode prefix", "ñandú", "ñan", true},
20+
}
21+
22+
for _, tc := range tests {
23+
t.Run(tc.name, func(t *testing.T) {
24+
got := HasPrefix(tc.s, tc.prefix)
25+
if got != tc.want {
26+
t.Fatalf("HasPrefix(%q, %q) = %v; want %v", tc.s, tc.prefix, got, tc.want)
27+
}
28+
})
29+
}
30+
}
31+
32+
func TestHasSuffix(t *testing.T) {
33+
tests := []struct {
34+
name string
35+
s string
36+
suffix string
37+
want bool
38+
}{
39+
{"empty suffix", "hello", "", true},
40+
{"exact match", "hello", "hello", true},
41+
{"short suffix", "hello", "lo", true},
42+
{"not a suffix", "hello", "hel", false},
43+
{"suffix longer than string", "go", "golang", false},
44+
{"single byte", "abc", "c", true},
45+
{"null byte suffix", "a\x00b", "\x00b", true},
46+
{"unicode suffix", "pingüino", "üino", true},
47+
}
48+
49+
for _, tc := range tests {
50+
t.Run(tc.name, func(t *testing.T) {
51+
got := HasSuffix(tc.s, tc.suffix)
52+
if got != tc.want {
53+
t.Fatalf("HasSuffix(%q, %q) = %v; want %v", tc.s, tc.suffix, got, tc.want)
54+
}
55+
})
56+
}
57+
}

0 commit comments

Comments
 (0)