• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

enetx / g / 19191360539

08 Nov 2025 09:45AM UTC coverage: 91.113% (-0.04%) from 91.157%
19191360539

push

github

enetx
new fns for String

99 of 105 new or added lines in 11 files covered. (94.29%)

6 existing lines in 2 files now uncovered.

6377 of 6999 relevant lines covered (91.11%)

628.01 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

95.13
/string.go
1
package g
2

3
import (
4
        "fmt"
5
        "math/big"
6
        "slices"
7
        "strconv"
8
        "strings"
9
        "unicode"
10
        "unicode/utf8"
11
        "unsafe"
12

13
        "github.com/enetx/g/cmp"
14
        "github.com/enetx/g/f"
15
        "golang.org/x/text/unicode/norm"
16
)
17

18
// NewString creates a new String from the provided string.
19
func NewString[T ~string | rune | byte | ~[]rune | ~[]byte](str T) String { return String(str) }
334✔
20

21
// Clone returns a copy of the String.
22
// It ensures that the returned String does not share underlying memory with the original String,
23
// making it safe to modify or store independently.
24
func (s String) Clone() String { return String(strings.Clone(s.Std())) }
1✔
25

26
// Transform applies a transformation function to the String and returns the result.
27
func (s String) Transform(fn func(String) String) String { return fn(s) }
1✔
28

29
// Builder returns a new Builder initialized with the content of the String.
30
func (s String) Builder() *Builder {
1✔
31
        b := new(Builder)
1✔
32
        b.WriteString(s)
1✔
33
        return b
1✔
34
}
1✔
35

36
// Min returns the minimum of Strings.
37
func (s String) Min(b ...String) String { return cmp.Min(append(b, s)...) }
3✔
38

39
// Max returns the maximum of Strings.
40
func (s String) Max(b ...String) String { return cmp.Max(append(b, s)...) }
3✔
41

42
// Random generates a random String of the specified length, selecting characters from predefined sets.
43
// If additional character sets are provided, only those will be used; the default set (ASCII_LETTERS and DIGITS)
44
// is excluded unless explicitly provided.
45
//
46
// Parameters:
47
// - count (Int): Length of the random String to generate.
48
// - letters (...String): Additional character sets to consider for generating the random String (optional).
49
//
50
// Returns:
51
// - String: Randomly generated String with the specified length.
52
//
53
// Example usage:
54
//
55
//        randomString := g.String.Random(10)
56
//        randomString contains a random String with 10 characters.
57
func (String) Random(length Int, letters ...String) String {
300✔
58
        var chars Slice[rune]
300✔
59

300✔
60
        if len(letters) != 0 {
300✔
NEW
61
                var b Builder
×
NEW
62

×
NEW
63
                for _, set := range letters {
×
NEW
64
                        _, _ = b.WriteString(set)
×
NEW
65
                }
×
66

NEW
67
                chars = b.String().Runes()
×
68
        } else {
300✔
69
                chars = (ASCII_LETTERS + DIGITS).Runes()
300✔
70
        }
300✔
71

72
        var b Builder
300✔
73
        b.Grow(length)
300✔
74

300✔
75
        for range length {
13,097✔
76
                b.WriteRune(chars.Random())
12,797✔
77
        }
12,797✔
78

79
        return b.String()
300✔
80
}
81

82
// IsASCII checks if all characters in the String are ASCII bytes.
83
func (s String) IsASCII() bool {
11✔
84
        for i := range s {
78✔
85
                if s[i] >= 0x80 {
72✔
86
                        return false
5✔
87
                }
5✔
88
        }
89

90
        return true
6✔
91
}
92

93
// IsDigit checks if all characters in the String are digits.
94
func (s String) IsDigit() bool {
4✔
95
        if s.Empty() {
5✔
96
                return false
1✔
97
        }
1✔
98

99
        for _, c := range s {
14✔
100
                if !unicode.IsDigit(c) {
12✔
101
                        return false
1✔
102
                }
1✔
103
        }
104

105
        return true
2✔
106
}
107

108
// ToInt tries to parse the String as an int and returns an Int.
109
func (s String) ToInt() Result[Int] {
395✔
110
        hint, err := strconv.ParseInt(s.Std(), 0, 64)
395✔
111
        if err != nil {
443✔
112
                return Err[Int](err)
48✔
113
        }
48✔
114

115
        return Ok(Int(hint))
347✔
116
}
117

118
// ToBigInt attempts to convert the String receiver into an Option containing a *big.Int.
119
// This function assumes the string represents a numerical value, which can be in decimal,
120
// hexadecimal (prefixed with "0x"), or octal (prefixed with "0") format. The function
121
// leverages the SetString method of the math/big package, automatically detecting the
122
// numeric base when set to 0.
123
//
124
// If the string is correctly formatted and represents a valid number, ToBigInt returns
125
// a Some containing the *big.Int parsed from the string. If the string is empty, contains
126
// invalid characters, or does not conform to a recognizable numeric format, ToBigInt
127
// returns a None, indicating that the conversion was unsuccessful.
128
//
129
// Returns:
130
//   - An Option[*big.Int] encapsulating the conversion result. It returns Some[*big.Int]
131
//     with the parsed value if successful, otherwise None[*big.Int] if the parsing fails.
132
func (s String) ToBigInt() Option[*big.Int] {
5✔
133
        if bigInt, ok := new(big.Int).SetString(s.Std(), 0); ok {
8✔
134
                return Some(bigInt)
3✔
135
        }
3✔
136

137
        return None[*big.Int]()
2✔
138
}
139

140
// ToFloat tries to parse the String as a float64 and returns an Float.
141
func (s String) ToFloat() Result[Float] {
8✔
142
        float, err := strconv.ParseFloat(s.Std(), 64)
8✔
143
        if err != nil {
13✔
144
                return Err[Float](err)
5✔
145
        }
5✔
146

147
        return Ok(Float(float))
3✔
148
}
149

150
// Title converts the String to title case.
151
func (s String) Title() String { return String(title.String(s.Std())) }
29✔
152

153
// Lower returns the String in lowercase.
154
func (s String) Lower() String { return s.Bytes().Lower().String() }
7✔
155

156
// Upper returns the String in uppercase.
157
func (s String) Upper() String { return s.Bytes().Upper().String() }
14✔
158

159
// IsLower checks if the String consists only of lowercase letters.
160
func (s String) IsLower() bool { return s.Bytes().IsLower() }
23✔
161

162
// IsUpper checks if the String consists only of uppercase letters.
163
func (s String) IsUpper() bool { return s.Bytes().IsUpper() }
22✔
164

165
// Trim removes leading and trailing white space from the String.
166
func (s String) Trim() String { return String(strings.TrimSpace(s.Std())) }
403✔
167

168
// TrimStart removes leading white space from the String.
169
func (s String) TrimStart() String { return String(strings.TrimLeftFunc(s.Std(), unicode.IsSpace)) }
5✔
170

171
// TrimEnd removes trailing white space from the String.
172
func (s String) TrimEnd() String { return String(strings.TrimRightFunc(s.Std(), unicode.IsSpace)) }
11✔
173

174
// TrimSet removes the specified set of characters from both the beginning and end of the String.
175
func (s String) TrimSet(cutset String) String { return String(strings.Trim(s.Std(), cutset.Std())) }
9✔
176

177
// TrimStartSet removes the specified set of characters from the beginning of the String.
178
func (s String) TrimStartSet(cutset String) String {
5✔
179
        return String(strings.TrimLeft(s.Std(), cutset.Std()))
5✔
180
}
5✔
181

182
// TrimEndSet removes the specified set of characters from the end of the String.
183
func (s String) TrimEndSet(cutset String) String {
5✔
184
        return String(strings.TrimRight(s.Std(), cutset.Std()))
5✔
185
}
5✔
186

187
// StripPrefix trims the specified prefix from the String.
188
func (s String) StripPrefix(prefix String) String {
4✔
189
        return String(strings.TrimPrefix(s.Std(), prefix.Std()))
4✔
190
}
4✔
191

192
// StripSuffix trims the specified suffix from the String.
193
func (s String) StripSuffix(suffix String) String {
8✔
194
        return String(strings.TrimSuffix(s.Std(), suffix.Std()))
8✔
195
}
8✔
196

197
// Replace replaces the 'oldS' String with the 'newS' String for the specified number of
198
// occurrences.
199
func (s String) Replace(oldS, newS String, n Int) String {
6✔
200
        return String(strings.Replace(s.Std(), oldS.Std(), newS.Std(), n.Std()))
6✔
201
}
6✔
202

203
// ReplaceAll replaces all occurrences of the 'oldS' String with the 'newS' String.
204
func (s String) ReplaceAll(oldS, newS String) String {
7✔
205
        return String(strings.ReplaceAll(s.Std(), oldS.Std(), newS.Std()))
7✔
206
}
7✔
207

208
// ReplaceMulti creates a custom replacer to perform multiple string replacements.
209
//
210
// Parameters:
211
//
212
// - oldnew ...String: Pairs of strings to be replaced. Specify as many pairs as needed.
213
//
214
// Returns:
215
//
216
// - String: A new string with replacements applied using the custom replacer.
217
//
218
// Example usage:
219
//
220
//        original := g.String("Hello, world! This is a test.")
221
//        replaced := original.ReplaceMulti(
222
//            "Hello", "Greetings",
223
//            "world", "universe",
224
//            "test", "example",
225
//        )
226
//        // replaced contains "Greetings, universe! This is an example."
227
func (s String) ReplaceMulti(oldnew ...String) String {
4✔
228
        pairs := make([]string, len(oldnew))
4✔
229
        for i, str := range oldnew {
20✔
230
                pairs[i] = str.Std()
16✔
231
        }
16✔
232

233
        return String(strings.NewReplacer(pairs...).Replace(s.Std()))
4✔
234
}
235

236
// Remove removes all occurrences of specified substrings from the String.
237
//
238
// Parameters:
239
//
240
// - matches ...String: Substrings to be removed from the string. Specify as many substrings as needed.
241
//
242
// Returns:
243
//
244
// - String: A new string with all specified substrings removed.
245
//
246
// Example usage:
247
//
248
//        original := g.String("Hello, world! This is a test.")
249
//        modified := original.Remove(
250
//            "Hello",
251
//            "test",
252
//        )
253
//        // modified contains ", world! This is a ."
254
func (s String) Remove(matches ...String) String {
3✔
255
        if len(matches) == 0 {
3✔
256
                return s
×
257
        }
×
258

259
        pairs := make([]string, len(matches)*2)
3✔
260
        for i, match := range matches {
9✔
261
                pairs[i*2] = match.Std()
6✔
262
                pairs[i*2+1] = ""
6✔
263
        }
6✔
264

265
        return String(strings.NewReplacer(pairs...).Replace(s.Std()))
3✔
266
}
267

268
// ReplaceNth returns a new String instance with the nth occurrence of oldS
269
// replaced with newS. If there aren't enough occurrences of oldS, the
270
// original String is returned. If n is less than -1, the original String
271
// is also returned. If n is -1, the last occurrence of oldS is replaced with newS.
272
//
273
// Returns:
274
//
275
// - A new String instance with the nth occurrence of oldS replaced with newS.
276
//
277
// Example usage:
278
//
279
//        s := g.String("The quick brown dog jumped over the lazy dog.")
280
//        result := s.ReplaceNth("dog", "fox", 2)
281
//        fmt.Println(result)
282
//
283
// Output: "The quick brown dog jumped over the lazy fox.".
284
func (s String) ReplaceNth(oldS, newS String, n Int) String {
28✔
285
        if n < -1 || len(oldS) == 0 {
31✔
286
                return s
3✔
287
        }
3✔
288

289
        count, i := Int(0), Int(0)
25✔
290

25✔
291
        for {
63✔
292
                pos := s[i:].Index(oldS)
38✔
293
                if pos == -1 {
43✔
294
                        break
5✔
295
                }
296

297
                pos += i
33✔
298
                count++
33✔
299

33✔
300
                if count == n || (n == -1 && s[pos+oldS.Len():].Index(oldS) == -1) {
53✔
301
                        return s[:pos] + newS + s[pos+oldS.Len():]
20✔
302
                }
20✔
303

304
                i = pos + oldS.Len()
13✔
305
        }
306

307
        return s
5✔
308
}
309

310
// Contains checks if the String contains the specified substring.
311
func (s String) Contains(substr String) bool { return f.Contains(substr)(s) }
10✔
312

313
// ContainsAny checks if the String contains any of the specified substrings.
314
func (s String) ContainsAny(substrs ...String) bool {
4✔
315
        return slices.ContainsFunc(substrs, s.Contains)
4✔
316
}
4✔
317

318
// ContainsAll checks if the given String contains all the specified substrings.
319
func (s String) ContainsAll(substrs ...String) bool {
4✔
320
        for _, substr := range substrs {
9✔
321
                if !s.Contains(substr) {
7✔
322
                        return false
2✔
323
                }
2✔
324
        }
325

326
        return true
2✔
327
}
328

329
// ContainsAnyChars checks if the String contains any characters from the specified String.
330
func (s String) ContainsAnyChars(chars String) bool { return f.ContainsAnyChars(chars)(s) }
5✔
331

332
// StartsWith checks if the String starts with the specified prefix.
333
// It uses a higher-order function to perform the check.
334
func (s String) StartsWith(prefix String) bool { return f.StartsWith(prefix)(s) }
7✔
335

336
// StartsWithAny checks if the String starts with any of the provided prefixes.
337
// The method accepts a variable number of arguments, allowing for checking against multiple
338
// prefixes at once. It iterates over the provided prefixes and uses the HasPrefix function from
339
// the strings package to check if the String starts with each prefix.
340
// The function returns true if the String starts with any of the prefixes, and false otherwise.
341
//
342
// Example usage:
343
//
344
//        s := g.String("http://example.com")
345
//        if s.StartsWithAny("http://", "https://") {
346
//           // do something
347
//        }
348
func (s String) StartsWithAny(prefixes ...String) bool {
5✔
349
        return slices.ContainsFunc(prefixes, s.StartsWith)
5✔
350
}
5✔
351

352
// EndsWith checks if the String ends with the specified suffix.
353
// It uses a higher-order function to perform the check.
354
func (s String) EndsWith(suffix String) bool { return f.EndsWith(suffix)(s) }
5✔
355

356
// EndsWithAny checks if the String ends with any of the provided suffixes.
357
// The method accepts a variable number of arguments, allowing for checking against multiple
358
// suffixes at once. It iterates over the provided suffixes and uses the HasSuffix function from
359
// the strings package to check if the String ends with each suffix.
360
// The function returns true if the String ends with any of the suffixes, and false otherwise.
361
//
362
// Example usage:
363
//
364
//        s := g.String("example.com")
365
//        if s.EndsWithAny(".com", ".net") {
366
//           // do something
367
//        }
368
func (s String) EndsWithAny(suffixes ...String) bool {
3✔
369
        return slices.ContainsFunc(suffixes, s.EndsWith)
3✔
370
}
3✔
371

372
// Lines splits the String by lines and returns the iterator.
373
func (s String) Lines() SeqSlice[String] {
2✔
374
        return transformSeq(strings.Lines(s.Std()), NewString).Map(String.TrimEnd)
2✔
375
}
2✔
376

377
// Fields splits the String into a slice of substrings, removing any whitespace, and returns the iterator.
378
func (s String) Fields() SeqSlice[String] {
4✔
379
        return transformSeq(strings.FieldsSeq(s.Std()), NewString)
4✔
380
}
4✔
381

382
// FieldsBy splits the String into a slice of substrings using a custom function to determine the field boundaries,
383
// and returns the iterator.
384
func (s String) FieldsBy(fn func(r rune) bool) SeqSlice[String] {
3✔
385
        return transformSeq(strings.FieldsFuncSeq(s.Std(), fn), NewString)
3✔
386
}
3✔
387

388
// Split splits the String by the specified separator and returns the iterator.
389
func (s String) Split(sep ...String) SeqSlice[String] {
110✔
390
        var separator String
110✔
391
        if len(sep) != 0 {
212✔
392
                separator = sep[0]
102✔
393
        }
102✔
394

395
        return transformSeq(strings.SplitSeq(s.Std(), separator.Std()), NewString)
110✔
396
}
397

398
// SplitAfter splits the String after each instance of the specified separator and returns the iterator.
399
func (s String) SplitAfter(sep String) SeqSlice[String] {
8✔
400
        return transformSeq(strings.SplitAfterSeq(s.Std(), sep.Std()), NewString)
8✔
401
}
8✔
402

403
// SplitN splits the String into substrings using the provided separator and returns an Slice[String] of the results.
404
// The n parameter controls the number of substrings to return:
405
// - If n is negative, there is no limit on the number of substrings returned.
406
// - If n is zero, an empty Slice[String] is returned.
407
// - If n is positive, at most n substrings are returned.
408
func (s String) SplitN(sep String, n Int) Slice[String] {
3✔
409
        return TransformSlice(strings.SplitN(s.Std(), sep.Std(), n.Std()), NewString)
3✔
410
}
3✔
411

412
// Chunks splits the String into chunks of the specified size.
413
//
414
// This function iterates through the String, creating new String chunks of the specified size.
415
// If size is less than or equal to 0 or the String is empty,
416
// it returns an empty Slice[String].
417
// If size is greater than or equal to the length of the String,
418
// it returns an Slice[String] containing the original String.
419
//
420
// Parameters:
421
//
422
// - size (Int): The size of the chunks to split the String into.
423
//
424
// Returns:
425
//
426
// - Slice[String]: A slice of String chunks of the specified size.
427
//
428
// Example usage:
429
//
430
//        text := g.String("Hello, World!")
431
//        chunks := text.Chunks(4)
432
//
433
// chunks contains {"Hell", "o, W", "orld", "!"}.
434
func (s String) Chunks(size Int) SeqSlice[String] {
6✔
435
        if size.Lte(0) || s.Empty() {
8✔
436
                return func(func(String) bool) {}
4✔
437
        }
438

439
        runes := s.Runes()
4✔
440
        if size.Gte(Int(len(runes))) {
6✔
441
                return func(yield func(String) bool) { yield(s) }
4✔
442
        }
443

444
        n := size.Std()
2✔
445
        return func(yield func(String) bool) {
4✔
446
                for i := 0; i < len(runes); i += n {
9✔
447
                        end := min(i+n, len(runes))
7✔
448
                        if !yield(String(runes[i:end])) {
7✔
449
                                return
×
450
                        }
×
451
                }
452
        }
453
}
454

455
// Cut returns two String values. The first String contains the remainder of the
456
// original String after the cut. The second String contains the text between the
457
// first occurrences of the 'start' and 'end' strings, with tags removed if specified.
458
//
459
// The function searches for the 'start' and 'end' strings within the String.
460
// If both are found, it returns the first String containing the remainder of the
461
// original String after the cut, followed by the second String containing the text
462
// between the first occurrences of 'start' and 'end' with tags removed if specified.
463
//
464
// If either 'start' or 'end' is empty or not found in the String, it returns the
465
// original String as the second String, and an empty String as the first.
466
//
467
// Parameters:
468
//
469
// - start (String): The String marking the beginning of the text to be cut.
470
//
471
// - end (String): The String marking the end of the text to be cut.
472
//
473
//   - rmtags (bool, optional): An optional boolean parameter indicating whether
474
//     to remove 'start' and 'end' tags from the cut text. Defaults to false.
475
//
476
// Returns:
477
//
478
//   - String: The first String containing the remainder of the original String
479
//     after the cut, with tags removed if specified,
480
//     or an empty String if 'start' or 'end' is empty or not found.
481
//
482
//   - String: The second String containing the text between the first occurrences of
483
//     'start' and 'end', or the original String if 'start' or 'end' is empty or not found.
484
//
485
// Example usage:
486
//
487
//        s := g.String("Hello, [world]! How are you?")
488
//        remainder, cut := s.Cut("[", "]")
489
//        // remainder: "Hello, ! How are you?"
490
//        // cut: "world"
491
func (s String) Cut(start, end String, rmtags ...bool) (String, String) {
10✔
492
        if start.Empty() || end.Empty() {
11✔
493
                return s, ""
1✔
494
        }
1✔
495

496
        startIndex := s.Index(start)
9✔
497
        if startIndex == -1 {
11✔
498
                return s, ""
2✔
499
        }
2✔
500

501
        startEnd := startIndex + start.Len()
7✔
502
        endIndex := s[startEnd:].Index(end)
7✔
503
        if endIndex == -1 {
8✔
504
                return s, ""
1✔
505
        }
1✔
506

507
        cut := s[startEnd : startEnd+endIndex]
6✔
508

6✔
509
        if len(rmtags) != 0 && !rmtags[0] {
6✔
510
                startEnd += end.Len()
×
511
                return s[:startIndex] + s[startIndex:startEnd+endIndex] + s[startEnd+endIndex:], cut
×
512
        }
×
513

514
        return s[:startIndex] + s[startEnd+endIndex+end.Len():], cut
6✔
515
}
516

517
// Similarity calculates the similarity between two Strings using the
518
// Levenshtein distance algorithm and returns the similarity percentage as an Float.
519
//
520
// The function compares two Strings using the Levenshtein distance,
521
// which measures the difference between two sequences by counting the number
522
// of single-character edits required to change one sequence into the other.
523
// The similarity is then calculated by normalizing the distance by the maximum
524
// length of the two input Strings.
525
//
526
// Parameters:
527
//
528
// - str (String): The String to compare with s.
529
//
530
// Returns:
531
//
532
// - Float: The similarity percentage between the two Strings as a value between 0 and 100.
533
//
534
// Example usage:
535
//
536
//        s1 := g.String("kitten")
537
//        s2 := g.String("sitting")
538
//        similarity := s1.Similarity(s2) // 57.14285714285714
539
func (s String) Similarity(str String) Float {
25✔
540
        if s.Eq(str) {
27✔
541
                return 100
2✔
542
        }
2✔
543

544
        if s.Empty() || str.Empty() {
24✔
545
                return 0
1✔
546
        }
1✔
547

548
        s1 := s.Runes()
22✔
549
        s2 := str.Runes()
22✔
550

22✔
551
        lenS1 := s.LenRunes()
22✔
552
        lenS2 := str.LenRunes()
22✔
553

22✔
554
        if lenS1 > lenS2 {
28✔
555
                s1, s2, lenS1, lenS2 = s2, s1, lenS2, lenS1
6✔
556
        }
6✔
557

558
        distance := NewSlice[Int](lenS1 + 1)
22✔
559

22✔
560
        for i, r2 := range s2 {
178✔
561
                prev := Int(i) + 1
156✔
562

156✔
563
                for j, r1 := range s1 {
1,224✔
564
                        current := distance[j]
1,068✔
565
                        if r2 != r1 {
1,981✔
566
                                current = distance[j].Add(1).Min(prev + 1).Min(distance[j+1] + 1)
913✔
567
                        }
913✔
568

569
                        distance[j], prev = prev, current
1,068✔
570
                }
571

572
                distance[lenS1] = prev
156✔
573
        }
574

575
        return Float(1).Sub(distance[lenS1].Float() / lenS1.Max(lenS2).Float()).Mul(100)
22✔
576
}
577

578
// Cmp compares two Strings and returns an cmp.Ordering indicating their relative order.
579
// The result will be cmp.Equal if s==str, cmp.Less if s < str, and cmp.Greater if s > str.
580
func (s String) Cmp(str String) cmp.Ordering { return cmp.Cmp(s, str) }
5✔
581

582
// Append appends the specified String to the current String.
583
func (s String) Append(str String) String { return s + str }
12✔
584

585
// Prepend prepends the specified String to the current String.
586
func (s String) Prepend(str String) String { return str + s }
4✔
587

588
// ContainsRune checks if the String contains the specified rune.
589
func (s String) ContainsRune(r rune) bool { return strings.ContainsRune(s.Std(), r) }
231✔
590

591
// Count returns the number of non-overlapping instances of the substring in the String.
592
func (s String) Count(substr String) Int { return Int(strings.Count(s.Std(), substr.Std())) }
3✔
593

594
// Empty checks if the String is empty.
595
func (s String) Empty() bool { return len(s) == 0 }
688✔
596

597
// Eq checks if two Strings are equal.
598
func (s String) Eq(str String) bool { return s == str }
161✔
599

600
// EqFold compares two String strings case-insensitively.
601
func (s String) EqFold(str String) bool { return strings.EqualFold(s.Std(), str.Std()) }
3✔
602

603
// Gt checks if the String is greater than the specified String.
604
func (s String) Gt(str String) bool { return s > str }
5✔
605

606
// Gte checks if the String is greater than or equal to the specified String.
607
func (s String) Gte(str String) bool { return s >= str }
5✔
608

609
// Bytes returns the String as an Bytes.
610
func (s String) Bytes() Bytes { return Bytes(s) }
327✔
611

612
// BytesUnsafe converts the String into Bytes without copying memory.
613
// Warning: the resulting Bytes shares the same underlying memory as the original String.
614
// If the original String is modified through unsafe operations (rare), or if it is garbage collected,
615
// the Bytes may become invalid or cause undefined behavior.
616
func (s String) BytesUnsafe() Bytes { return unsafe.Slice(unsafe.StringData(s.Std()), len(s)) }
1✔
617

618
// Index returns the index of the first instance of the specified substring in the String, or -1
619
// if substr is not present in s.
620
func (s String) Index(substr String) Int { return Int(strings.Index(s.Std(), substr.Std())) }
1,323✔
621

622
// LastIndex returns the index of the last instance of the specified substring in the String, or -1
623
// if substr is not present in s.
624
func (s String) LastIndex(substr String) Int { return Int(strings.LastIndex(s.Std(), substr.Std())) }
35✔
625

626
// IndexRune returns the index of the first instance of the specified rune in the String.
627
func (s String) IndexRune(r rune) Int { return Int(strings.IndexRune(s.Std(), r)) }
3✔
628

629
// Len returns the length of the String.
630
func (s String) Len() Int { return Int(len(s)) }
909✔
631

632
// LenRunes returns the number of runes in the String.
633
func (s String) LenRunes() Int { return Int(utf8.RuneCountInString(s.Std())) }
75✔
634

635
// Lt checks if the String is less than the specified String.
636
func (s String) Lt(str String) bool { return s < str }
5✔
637

638
// Lte checks if the String is less than or equal to the specified String.
639
func (s String) Lte(str String) bool { return s <= str }
5✔
640

641
// Map applies the provided function to all runes in the String and returns the resulting String.
642
func (s String) Map(fn func(rune) rune) String { return String(strings.Map(fn, s.Std())) }
9✔
643

644
// NormalizeNFC returns a new String with its Unicode characters normalized using the NFC form.
645
func (s String) NormalizeNFC() String { return String(norm.NFC.String(s.Std())) }
13✔
646

647
// Ne checks if two Strings are not equal.
648
func (s String) Ne(str String) bool { return !s.Eq(str) }
80✔
649

650
// NotEmpty checks if the String is not empty.
651
func (s String) NotEmpty() bool { return s.Len() != 0 }
388✔
652

653
// Reader returns a *strings.Reader initialized with the content of String.
654
func (s String) Reader() *strings.Reader { return strings.NewReader(s.Std()) }
28✔
655

656
// Repeat returns a new String consisting of the specified count of the original String.
657
func (s String) Repeat(count Int) String { return String(strings.Repeat(s.Std(), count.Std())) }
7✔
658

659
// Reverse reverses the String.
660
func (s String) Reverse() String { return s.Bytes().Reverse().String() }
35✔
661

662
// Runes returns the String as a slice of runes.
663
func (s String) Runes() Slice[rune] { return []rune(s) }
367✔
664

665
// Chars splits the String into individual characters and returns the iterator.
666
func (s String) Chars() SeqSlice[String] { return s.Split() }
7✔
667

668
// SubString extracts a substring from the String starting at the 'start' index and ending before the 'end' index.
669
// The function also supports an optional 'step' parameter to define the increment between indices in the substring.
670
// If 'start' or 'end' index is negative, they represent positions relative to the end of the String:
671
// - A negative 'start' index indicates the position from the end of the String, moving backward.
672
// - A negative 'end' index indicates the position from the end of the String.
673
// The function ensures that indices are adjusted to fall within the valid range of the String's length.
674
// If indices are out of bounds or if 'start' exceeds 'end', the function returns the original String unmodified.
675
func (s String) SubString(start, end Int, step ...Int) String {
6✔
676
        return String(s.Runes().SubSlice(start, end, step...))
6✔
677
}
6✔
678

679
// Std returns the String as a string.
680
func (s String) Std() string { return string(s) }
6,882✔
681

682
// Format applies a specified format to the String object.
683
func (s String) Format(template String) String { return Format(template, s) }
4✔
684

685
// Truncate shortens the String to the specified maximum length. If the String exceeds the
686
// specified length, it is truncated, and an ellipsis ("...") is appended to indicate the truncation.
687
//
688
// If the length of the String is less than or equal to the specified maximum length, the
689
// original String is returned unchanged.
690
//
691
// The method respects Unicode characters and truncates based on the number of runes,
692
// not bytes.
693
//
694
// Parameters:
695
//   - max: The maximum number of runes allowed in the resulting String.
696
//
697
// Returns:
698
//   - A new String truncated to the specified maximum length with "..." appended
699
//     if truncation occurs. Otherwise, returns the original String.
700
//
701
// Example usage:
702
//
703
//        s := g.String("Hello, World!")
704
//        result := s.Truncate(5)
705
//        // result: "Hello..."
706
//
707
//        s2 := g.String("Short")
708
//        result2 := s2.Truncate(10)
709
//        // result2: "Short"
710
//
711
//        s3 := g.String("😊😊😊😊😊")
712
//        result3 := s3.Truncate(3)
713
//        // result3: "😊😊😊..."
714
func (s String) Truncate(max Int) String {
11✔
715
        if max.IsNegative() || s.LenRunes().Lte(max) {
17✔
716
                return s
6✔
717
        }
6✔
718

719
        return String(s.Runes().SubSlice(0, max)).Append("...")
5✔
720
}
721

722
// LeftJustify justifies the String to the left by adding padding to the right, up to the
723
// specified length. If the length of the String is already greater than or equal to the specified
724
// length, or the pad is empty, the original String is returned.
725
//
726
// The padding String is repeated as necessary to fill the remaining length.
727
// The padding is added to the right of the String.
728
//
729
// Parameters:
730
//   - length: The desired length of the resulting justified String.
731
//   - pad: The String used as padding.
732
//
733
// Example usage:
734
//
735
//        s := g.String("Hello")
736
//        result := s.LeftJustify(10, "...")
737
//        // result: "Hello....."
738
func (s String) LeftJustify(length Int, pad String) String {
4✔
739
        rlen := s.LenRunes()
4✔
740
        if rlen >= length || pad.Empty() {
7✔
741
                return s
3✔
742
        }
3✔
743

744
        var b Builder
1✔
745

1✔
746
        _, _ = b.WriteString(s)
1✔
747
        writePadding(&b, pad, pad.LenRunes(), length-rlen)
1✔
748

1✔
749
        return b.String()
1✔
750
}
751

752
// RightJustify justifies the String to the right by adding padding to the left, up to the
753
// specified length. If the length of the String is already greater than or equal to the specified
754
// length, or the pad is empty, the original String is returned.
755
//
756
// The padding String is repeated as necessary to fill the remaining length.
757
// The padding is added to the left of the String.
758
//
759
// Parameters:
760
//   - length: The desired length of the resulting justified String.
761
//   - pad: The String used as padding.
762
//
763
// Example usage:
764
//
765
//        s := g.String("Hello")
766
//        result := s.RightJustify(10, "...")
767
//        // result: ".....Hello"
768
func (s String) RightJustify(length Int, pad String) String {
4✔
769
        rlen := s.LenRunes()
4✔
770
        if rlen >= length || pad.Empty() {
7✔
771
                return s
3✔
772
        }
3✔
773

774
        var b Builder
1✔
775

1✔
776
        writePadding(&b, pad, pad.LenRunes(), length-rlen)
1✔
777
        _, _ = b.WriteString(s)
1✔
778

1✔
779
        return b.String()
1✔
780
}
781

782
// Center justifies the String by adding padding on both sides, up to the specified length.
783
// If the length of the String is already greater than or equal to the specified length, or the
784
// pad is empty, the original String is returned.
785
//
786
// The padding String is repeated as necessary to evenly distribute the remaining length on both
787
// sides.
788
// The padding is added to the left and right of the String.
789
//
790
// Parameters:
791
//   - length: The desired length of the resulting justified String.
792
//   - pad: The String used as padding.
793
//
794
// Example usage:
795
//
796
//        s := g.String("Hello")
797
//        result := s.Center(10, "...")
798
//        // result: "..Hello..."
799
func (s String) Center(length Int, pad String) String {
4✔
800
        if s.LenRunes() >= length || pad.Empty() {
7✔
801
                return s
3✔
802
        }
3✔
803

804
        var b Builder
1✔
805

1✔
806
        remains := length - s.LenRunes()
1✔
807

1✔
808
        writePadding(&b, pad, pad.LenRunes(), remains/2)
1✔
809
        _, _ = b.WriteString(s)
1✔
810
        writePadding(&b, pad, pad.LenRunes(), (remains+1)/2)
1✔
811

1✔
812
        return b.String()
1✔
813
}
814

815
// writePadding writes the padding String to the output Builder to fill the remaining length.
816
// It repeats the padding String as necessary and appends any remaining runes from the padding
817
// String.
818
func writePadding(b *Builder, pad String, padlen, remains Int) {
4✔
819
        if repeats := remains / padlen; repeats > 0 {
8✔
820
                _, _ = b.WriteString(pad.Repeat(repeats))
4✔
821
        }
4✔
822

823
        padrunes := pad.Runes()
4✔
824
        for i := range remains % padlen {
4✔
825
                _, _ = b.WriteRune(padrunes[i])
×
826
        }
×
827
}
828

829
// Print writes the content of the String to the standard output (console)
830
// and returns the String unchanged.
831
func (s String) Print() String { fmt.Print(s); return s }
1✔
832

833
// Println writes the content of the String to the standard output (console) with a newline
834
// and returns the String unchanged.
835
func (s String) Println() String { fmt.Println(s); return s }
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc