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

enetx / g / 14661178371

25 Apr 2025 09:14AM UTC coverage: 86.248% (-0.2%) from 86.478%
14661178371

push

github

enetx
ref

124 of 174 new or added lines in 13 files covered. (71.26%)

1 existing line in 1 file now uncovered.

3462 of 4014 relevant lines covered (86.25%)

127.49 hits per line

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

91.28
/string.go
1
package g
2

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

11
        "github.com/enetx/g/cmp"
12
        "github.com/enetx/g/f"
13
        "golang.org/x/text/cases"
14
        "golang.org/x/text/language"
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) }
36✔
20

21
// Ptr returns a pointer to the current String value.
22
func (s String) Ptr() *String { return &s }
1✔
23

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

27
// Builder returns a new Builder initialized with the content of the String.
28
func (s String) Builder() *Builder { return NewBuilder().Write(s) }
1✔
29

30
// Min returns the minimum of Strings.
31
func (s String) Min(b ...String) String { return cmp.Min(append(b, s)...) }
3✔
32

33
// Max returns the maximum of Strings.
34
func (s String) Max(b ...String) String { return cmp.Max(append(b, s)...) }
3✔
35

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

300✔
54
        if len(letters) != 0 {
300✔
55
                chars = letters[0].Runes()
×
56
        } else {
300✔
57
                chars = (ASCII_LETTERS + DIGITS).Runes()
300✔
58
        }
300✔
59

60
        result := NewBuilder()
300✔
61

300✔
62
        for range length {
12,534✔
63
                result.WriteRune(chars.Random())
12,234✔
64
        }
12,234✔
65

66
        return result.String()
300✔
67
}
68

69
// IsASCII checks if all characters in the String are ASCII bytes.
70
func (s String) IsASCII() bool {
8✔
71
        for _, r := range s {
72✔
72
                if r > unicode.MaxASCII {
67✔
73
                        return false
3✔
74
                }
3✔
75
        }
76

77
        return true
5✔
78
}
79

80
// IsDigit checks if all characters in the String are digits.
81
func (s String) IsDigit() bool {
4✔
82
        if s.Empty() {
5✔
83
                return false
1✔
84
        }
1✔
85

86
        for _, c := range s {
14✔
87
                if !unicode.IsDigit(c) {
12✔
88
                        return false
1✔
89
                }
1✔
90
        }
91

92
        return true
2✔
93
}
94

95
// ToInt tries to parse the String as an int and returns an Int.
96
func (s String) ToInt() Result[Int] {
210✔
97
        hint, err := strconv.ParseInt(s.Std(), 0, 32)
210✔
98
        if err != nil {
256✔
99
                return Err[Int](err)
46✔
100
        }
46✔
101

102
        return Ok(Int(hint))
164✔
103
}
104

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

124
        return None[*big.Int]()
2✔
125
}
126

127
// ToFloat tries to parse the String as a float64 and returns an Float.
128
func (s String) ToFloat() Result[Float] {
8✔
129
        float, err := strconv.ParseFloat(s.Std(), 64)
8✔
130
        if err != nil {
13✔
131
                return Err[Float](err)
5✔
132
        }
5✔
133

134
        return Ok(Float(float))
3✔
135
}
136

137
// Title converts the String to title case.
138
func (s String) Title() String { return String(cases.Title(language.English).String(s.Std())) }
29✔
139

140
// Lower returns the String in lowercase.
141
func (s String) Lower() String { return String(cases.Lower(language.English).String(s.Std())) }
7✔
142

143
// Upper returns the String in uppercase.
144
func (s String) Upper() String { return String(cases.Upper(language.English).String(s.Std())) }
8✔
145

146
// Trim removes leading and trailing white space from the String.
147
func (s String) Trim() String { return String(strings.TrimSpace(s.Std())) }
209✔
148

149
// TrimStart removes leading white space from the String.
150
func (s String) TrimStart() String { return trimStringStart(s) }
5✔
151

152
// TrimEnd removes trailing white space from the String.
153
func (s String) TrimEnd() String { return trimStringEnd(s) }
5✔
154

155
// TrimSet removes the specified set of characters from both the beginning and end of the String.
156
func (s String) TrimSet(cutset String) String {
9✔
157
        return String(strings.Trim(s.Std(), cutset.Std()))
9✔
158
}
9✔
159

160
// TrimStartSet removes the specified set of characters from the beginning of the String.
161
func (s String) TrimStartSet(cutset String) String {
5✔
162
        return String(strings.TrimLeft(s.Std(), cutset.Std()))
5✔
163
}
5✔
164

165
// TrimEndSet removes the specified set of characters from the end of the String.
166
func (s String) TrimEndSet(cutset String) String {
10✔
167
        return String(strings.TrimRight(s.Std(), cutset.Std()))
10✔
168
}
10✔
169

170
// StripPrefix trims the specified prefix from the String.
171
func (s String) StripPrefix(prefix String) String {
4✔
172
        return String(strings.TrimPrefix(s.Std(), prefix.Std()))
4✔
173
}
4✔
174

175
// StripSuffix trims the specified suffix from the String.
176
func (s String) StripSuffix(suffix String) String {
13✔
177
        return String(strings.TrimSuffix(s.Std(), suffix.Std()))
13✔
178
}
13✔
179

180
// Replace replaces the 'oldS' String with the 'newS' String for the specified number of
181
// occurrences.
182
func (s String) Replace(oldS, newS String, n Int) String {
6✔
183
        return String(strings.Replace(s.Std(), oldS.Std(), newS.Std(), n.Std()))
6✔
184
}
6✔
185

186
// ReplaceAll replaces all occurrences of the 'oldS' String with the 'newS' String.
187
func (s String) ReplaceAll(oldS, newS String) String {
11✔
188
        return String(strings.ReplaceAll(s.Std(), oldS.Std(), newS.Std()))
11✔
189
}
11✔
190

191
// ReplaceMulti creates a custom replacer to perform multiple string replacements.
192
//
193
// Parameters:
194
//
195
// - oldnew ...String: Pairs of strings to be replaced. Specify as many pairs as needed.
196
//
197
// Returns:
198
//
199
// - String: A new string with replacements applied using the custom replacer.
200
//
201
// Example usage:
202
//
203
//        original := g.String("Hello, world! This is a test.")
204
//        replaced := original.ReplaceMulti(
205
//            "Hello", "Greetings",
206
//            "world", "universe",
207
//            "test", "example",
208
//        )
209
//        // replaced contains "Greetings, universe! This is an example."
210
func (s String) ReplaceMulti(oldnew ...String) String {
4✔
211
        on := Slice[String](oldnew).ToStringSlice()
4✔
212
        return String(strings.NewReplacer(on...).Replace(s.Std()))
4✔
213
}
4✔
214

215
// Remove removes all occurrences of specified substrings from the String.
216
//
217
// Parameters:
218
//
219
// - matches ...String: Substrings to be removed from the string. Specify as many substrings as needed.
220
//
221
// Returns:
222
//
223
// - String: A new string with all specified substrings removed.
224
//
225
// Example usage:
226
//
227
//        original := g.String("Hello, world! This is a test.")
228
//        modified := original.Remove(
229
//            "Hello",
230
//            "test",
231
//        )
232
//        // modified contains ", world! This is a ."
233
func (s String) Remove(matches ...String) String {
3✔
234
        for _, match := range matches {
9✔
235
                s = s.ReplaceAll(match, "")
6✔
236
        }
6✔
237

238
        return s
3✔
239
}
240

241
// ReplaceNth returns a new String instance with the nth occurrence of oldS
242
// replaced with newS. If there aren't enough occurrences of oldS, the
243
// original String is returned. If n is less than -1, the original String
244
// is also returned. If n is -1, the last occurrence of oldS is replaced with newS.
245
//
246
// Returns:
247
//
248
// - A new String instance with the nth occurrence of oldS replaced with newS.
249
//
250
// Example usage:
251
//
252
//        s := g.String("The quick brown dog jumped over the lazy dog.")
253
//        result := s.ReplaceNth("dog", "fox", 2)
254
//        fmt.Println(result)
255
//
256
// Output: "The quick brown dog jumped over the lazy fox.".
257
func (s String) ReplaceNth(oldS, newS String, n Int) String {
18✔
258
        if n < -1 || len(oldS) == 0 {
21✔
259
                return s
3✔
260
        }
3✔
261

262
        count, i := Int(0), Int(0)
15✔
263

15✔
264
        for {
35✔
265
                pos := s[i:].Index(oldS)
20✔
266
                if pos == -1 {
24✔
267
                        break
4✔
268
                }
269

270
                pos += i
16✔
271
                count++
16✔
272

16✔
273
                if count == n || (n == -1 && s[pos+oldS.Len():].Index(oldS) == -1) {
27✔
274
                        return s[:pos] + newS + s[pos+oldS.Len():]
11✔
275
                }
11✔
276

277
                i = pos + oldS.Len()
5✔
278
        }
279

280
        return s
4✔
281
}
282

283
// Contains checks if the String contains the specified substring.
284
func (s String) Contains(substr String) bool { return f.Contains(substr)(s) }
10✔
285

286
// ContainsAny checks if the String contains any of the specified substrings.
287
func (s String) ContainsAny(substrs ...String) bool {
4✔
288
        return Slice[String](substrs).
4✔
289
                Iter().
4✔
290
                Any(func(substr String) bool { return s.Contains(substr) })
9✔
291
}
292

293
// ContainsAll checks if the given String contains all the specified substrings.
294
func (s String) ContainsAll(substrs ...String) bool {
4✔
295
        return Slice[String](substrs).
4✔
296
                Iter().
4✔
297
                All(func(substr String) bool { return s.Contains(substr) })
9✔
298
}
299

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

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

307
// StartsWithAny checks if the String starts with any of the provided prefixes.
308
// The method accepts a variable number of arguments, allowing for checking against multiple
309
// prefixes at once. It iterates over the provided prefixes and uses the HasPrefix function from
310
// the strings package to check if the String starts with each prefix.
311
// The function returns true if the String starts with any of the prefixes, and false otherwise.
312
//
313
// Example usage:
314
//
315
//        s := g.String("http://example.com")
316
//        if s.StartsWithAny("http://", "https://") {
317
//           // do something
318
//        }
319
func (s String) StartsWithAny(prefixes ...String) bool {
5✔
320
        return Slice[String](prefixes).
5✔
321
                Iter().
5✔
322
                Any(func(prefix String) bool { return s.StartsWith(prefix) })
12✔
323
}
324

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

329
// EndsWithAny checks if the String ends with any of the provided suffixes.
330
// The method accepts a variable number of arguments, allowing for checking against multiple
331
// suffixes at once. It iterates over the provided suffixes and uses the HasSuffix function from
332
// the strings package to check if the String ends with each suffix.
333
// The function returns true if the String ends with any of the suffixes, and false otherwise.
334
//
335
// Example usage:
336
//
337
//        s := g.String("example.com")
338
//        if s.EndsWithAny(".com", ".net") {
339
//           // do something
340
//        }
341
func (s String) EndsWithAny(suffixes ...String) bool {
3✔
342
        return Slice[String](suffixes).
3✔
343
                Iter().
3✔
344
                Any(func(suffix String) bool { return s.EndsWith(suffix) })
8✔
345
}
346

347
// Lines splits the String by lines and returns the iterator.
348
func (s String) Lines() SeqSlice[String] { return linesString(s) }
2✔
349

350
// Fields splits the String into a slice of substrings, removing any whitespace, and returns the iterator.
351
func (s String) Fields() SeqSlice[String] { return fieldsString(s) }
4✔
352

353
// FieldsBy splits the String into a slice of substrings using a custom function to determine the field boundaries,
354
// and returns the iterator.
355
func (s String) FieldsBy(fn func(r rune) bool) SeqSlice[String] { return fieldsbyString(s, fn) }
3✔
356

357
// Split splits the String by the specified separator and returns the iterator.
358
func (s String) Split(sep ...String) SeqSlice[String] {
101✔
359
        var separator String
101✔
360
        if len(sep) != 0 {
193✔
361
                separator = sep[0]
92✔
362
        }
92✔
363

364
        return splitString(s, separator, 0)
101✔
365
}
366

367
// SplitAfter splits the String after each instance of the specified separator and returns the iterator.
368
func (s String) SplitAfter(sep String) SeqSlice[String] { return splitString(s, sep, sep.Len()) }
8✔
369

370
// SplitN splits the String into substrings using the provided separator and returns an Slice[String] of the results.
371
// The n parameter controls the number of substrings to return:
372
// - If n is negative, there is no limit on the number of substrings returned.
373
// - If n is zero, an empty Slice[String] is returned.
374
// - If n is positive, at most n substrings are returned.
375
func (s String) SplitN(sep String, n Int) Slice[String] {
3✔
376
        return TransformSlice(strings.SplitN(s.Std(), sep.Std(), n.Std()), NewString)
3✔
377
}
3✔
378

379
// Chunks splits the String into chunks of the specified size.
380
//
381
// This function iterates through the String, creating new String chunks of the specified size.
382
// If size is less than or equal to 0 or the String is empty,
383
// it returns an empty Slice[String].
384
// If size is greater than or equal to the length of the String,
385
// it returns an Slice[String] containing the original String.
386
//
387
// Parameters:
388
//
389
// - size (Int): The size of the chunks to split the String into.
390
//
391
// Returns:
392
//
393
// - Slice[String]: A slice of String chunks of the specified size.
394
//
395
// Example usage:
396
//
397
//        text := g.String("Hello, World!")
398
//        chunks := text.Chunks(4)
399
//
400
// chunks contains {"Hell", "o, W", "orld", "!"}.
401
func (s String) Chunks(size Int) Slice[String] {
6✔
402
        if size <= 0 || s.Empty() {
8✔
403
                return nil
2✔
404
        }
2✔
405

406
        if size >= s.Len() {
6✔
407
                return Slice[String]{s}
2✔
408
        }
2✔
409

410
        return TransformSlice(s.Split().Chunks(size).Collect(), func(ch Slice[String]) String { return ch.Join() })
9✔
411
}
412

413
// Cut returns two String values. The first String contains the remainder of the
414
// original String after the cut. The second String contains the text between the
415
// first occurrences of the 'start' and 'end' strings, with tags removed if specified.
416
//
417
// The function searches for the 'start' and 'end' strings within the String.
418
// If both are found, it returns the first String containing the remainder of the
419
// original String after the cut, followed by the second String containing the text
420
// between the first occurrences of 'start' and 'end' with tags removed if specified.
421
//
422
// If either 'start' or 'end' is empty or not found in the String, it returns the
423
// original String as the second String, and an empty String as the first.
424
//
425
// Parameters:
426
//
427
// - start (String): The String marking the beginning of the text to be cut.
428
//
429
// - end (String): The String marking the end of the text to be cut.
430
//
431
//   - rmtags (bool, optional): An optional boolean parameter indicating whether
432
//     to remove 'start' and 'end' tags from the cut text. Defaults to false.
433
//
434
// Returns:
435
//
436
//   - String: The first String containing the remainder of the original String
437
//     after the cut, with tags removed if specified,
438
//     or an empty String if 'start' or 'end' is empty or not found.
439
//
440
//   - String: The second String containing the text between the first occurrences of
441
//     'start' and 'end', or the original String if 'start' or 'end' is empty or not found.
442
//
443
// Example usage:
444
//
445
//        s := g.String("Hello, [world]! How are you?")
446
//        remainder, cut := s.Cut("[", "]")
447
//        // remainder: "Hello, ! How are you?"
448
//        // cut: "world"
449
func (s String) Cut(start, end String, rmtags ...bool) (String, String) {
10✔
450
        if start.Empty() || end.Empty() {
11✔
451
                return s, ""
1✔
452
        }
1✔
453

454
        startIndex := s.Index(start)
9✔
455
        if startIndex == -1 {
11✔
456
                return s, ""
2✔
457
        }
2✔
458

459
        endIndex := s[startIndex+start.Len():].Index(end)
7✔
460
        if endIndex == -1 {
8✔
461
                return s, ""
1✔
462
        }
1✔
463

464
        cut := s[startIndex+start.Len() : startIndex+start.Len()+endIndex]
6✔
465

6✔
466
        startCutIndex := startIndex
6✔
467
        endCutIndex := startIndex + start.Len() + endIndex
6✔
468

6✔
469
        if len(rmtags) != 0 && !rmtags[0] {
6✔
470
                startCutIndex += start.Len()
×
471
        } else {
6✔
472
                endCutIndex += end.Len()
6✔
473
        }
6✔
474

475
        remainder := s[:startCutIndex] + s[endCutIndex:]
6✔
476

6✔
477
        return remainder, cut
6✔
478
}
479

480
// Similarity calculates the similarity between two Strings using the
481
// Levenshtein distance algorithm and returns the similarity percentage as an Float.
482
//
483
// The function compares two Strings using the Levenshtein distance,
484
// which measures the difference between two sequences by counting the number
485
// of single-character edits required to change one sequence into the other.
486
// The similarity is then calculated by normalizing the distance by the maximum
487
// length of the two input Strings.
488
//
489
// Parameters:
490
//
491
// - str (String): The String to compare with s.
492
//
493
// Returns:
494
//
495
// - Float: The similarity percentage between the two Strings as a value between 0 and 100.
496
//
497
// Example usage:
498
//
499
//        s1 := g.String("kitten")
500
//        s2 := g.String("sitting")
501
//        similarity := s1.Similarity(s2) // 57.14285714285714
502
func (s String) Similarity(str String) Float {
25✔
503
        if s.Eq(str) {
27✔
504
                return 100
2✔
505
        }
2✔
506

507
        if s.Empty() || str.Empty() {
24✔
508
                return 0
1✔
509
        }
1✔
510

511
        s1 := s.Runes()
22✔
512
        s2 := str.Runes()
22✔
513

22✔
514
        lenS1 := s.LenRunes()
22✔
515
        lenS2 := str.LenRunes()
22✔
516

22✔
517
        if lenS1 > lenS2 {
28✔
518
                s1, s2, lenS1, lenS2 = s2, s1, lenS2, lenS1
6✔
519
        }
6✔
520

521
        distance := NewSlice[Int](lenS1 + 1)
22✔
522

22✔
523
        for i, r2 := range s2 {
178✔
524
                prev := Int(i) + 1
156✔
525

156✔
526
                for j, r1 := range s1 {
1,224✔
527
                        current := distance[j]
1,068✔
528
                        if r2 != r1 {
1,981✔
529
                                current = distance[j].Add(1).Min(prev + 1).Min(distance[j+1] + 1)
913✔
530
                        }
913✔
531

532
                        distance[j], prev = prev, current
1,068✔
533
                }
534

535
                distance[lenS1] = prev
156✔
536
        }
537

538
        return Float(1).Sub(distance[lenS1].Float() / lenS1.Max(lenS2).Float()).Mul(100)
22✔
539
}
540

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

545
// Append appends the specified String to the current String.
546
func (s String) Append(str String) String { return s + str }
9✔
547

548
// Prepend prepends the specified String to the current String.
549
func (s String) Prepend(str String) String { return str + s }
4✔
550

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

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

557
// Empty checks if the String is empty.
558
func (s String) Empty() bool { return len(s) == 0 }
490✔
559

560
// Eq checks if two Strings are equal.
561
func (s String) Eq(str String) bool { return s == str }
149✔
562

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

566
// Gt checks if the String is greater than the specified String.
567
func (s String) Gt(str String) bool { return s > str }
5✔
568

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

572
// Bytes returns the String as an Bytes.
573
func (s String) Bytes() Bytes { return Bytes(s) }
268✔
574

575
// Index returns the index of the first instance of the specified substring in the String, or -1
576
// if substr is not present in s.
577
func (s String) Index(substr String) Int { return Int(strings.Index(s.Std(), substr.Std())) }
917✔
578

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

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

586
// Len returns the length of the String.
587
func (s String) Len() Int { return Int(len(s)) }
630✔
588

589
// LenRunes returns the number of runes in the String.
590
func (s String) LenRunes() Int { return Int(utf8.RuneCountInString(s.Std())) }
77✔
591

592
// Lt checks if the String is less than the specified String.
593
func (s String) Lt(str String) bool { return s < str }
5✔
594

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

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

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

604
// Ne checks if two Strings are not equal.
605
func (s String) Ne(str String) bool { return !s.Eq(str) }
74✔
606

607
// NotEmpty checks if the String is not empty.
608
func (s String) NotEmpty() bool { return s.Len() != 0 }
204✔
609

610
// Reader returns a *strings.Reader initialized with the content of String.
611
func (s String) Reader() *strings.Reader { return strings.NewReader(s.Std()) }
11✔
612

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

616
// Reverse reverses the String.
617
func (s String) Reverse() String { return s.Bytes().Reverse().String() }
35✔
618

619
// Runes returns the String as a slice of runes.
620
func (s String) Runes() Slice[rune] { return []rune(s) }
363✔
621

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

625
// SubString extracts a substring from the String starting at the 'start' index and ending before the 'end' index.
626
// The function also supports an optional 'step' parameter to define the increment between indices in the substring.
627
// If 'start' or 'end' index is negative, they represent positions relative to the end of the String:
628
// - A negative 'start' index indicates the position from the end of the String, moving backward.
629
// - A negative 'end' index indicates the position from the end of the String.
630
// The function ensures that indices are adjusted to fall within the valid range of the String's length.
631
// If indices are out of bounds or if 'start' exceeds 'end', the function returns the original String unmodified.
632
func (s String) SubString(start, end Int, step ...Int) String {
6✔
633
        return String(s.Runes().SubSlice(start, end, step...))
6✔
634
}
6✔
635

636
// Std returns the String as a string.
637
func (s String) Std() string { return string(s) }
4,090✔
638

639
// Format applies a specified format to the String object.
640
func (s String) Format(template String) String { return Format(template, s) }
8✔
641

642
// Truncate shortens the String to the specified maximum length. If the String exceeds the
643
// specified length, it is truncated, and an ellipsis ("...") is appended to indicate the truncation.
644
//
645
// If the length of the String is less than or equal to the specified maximum length, the
646
// original String is returned unchanged.
647
//
648
// The method respects Unicode characters and truncates based on the number of runes,
649
// not bytes.
650
//
651
// Parameters:
652
//   - max: The maximum number of runes allowed in the resulting String.
653
//
654
// Returns:
655
//   - A new String truncated to the specified maximum length with "..." appended
656
//     if truncation occurs. Otherwise, returns the original String.
657
//
658
// Example usage:
659
//
660
//        s := g.String("Hello, World!")
661
//        result := s.Truncate(5)
662
//        // result: "Hello..."
663
//
664
//        s2 := g.String("Short")
665
//        result2 := s2.Truncate(10)
666
//        // result2: "Short"
667
//
668
//        s3 := g.String("😊😊😊😊😊")
669
//        result3 := s3.Truncate(3)
670
//        // result3: "😊😊😊..."
671
func (s String) Truncate(max Int) String {
11✔
672
        if max.IsNegative() || s.LenRunes().Lte(max) {
17✔
673
                return s
6✔
674
        }
6✔
675

676
        return String(s.Runes().SubSlice(0, max)).Append("...")
5✔
677
}
678

679
// LeftJustify justifies the String to the left by adding padding to the right, up to the
680
// specified length. If the length of the String is already greater than or equal to the specified
681
// length, or the pad is empty, the original String is returned.
682
//
683
// The padding String is repeated as necessary to fill the remaining length.
684
// The padding is added to the right of the String.
685
//
686
// Parameters:
687
//   - length: The desired length of the resulting justified String.
688
//   - pad: The String used as padding.
689
//
690
// Example usage:
691
//
692
//        s := g.String("Hello")
693
//        result := s.LeftJustify(10, "...")
694
//        // result: "Hello....."
695
func (s String) LeftJustify(length Int, pad String) String {
4✔
696
        if s.LenRunes() >= length || pad.Eq("") {
7✔
697
                return s
3✔
698
        }
3✔
699

700
        output := NewBuilder()
1✔
701

1✔
702
        _ = output.Write(s)
1✔
703
        writePadding(output, pad, pad.LenRunes(), length-s.LenRunes())
1✔
704

1✔
705
        return output.String()
1✔
706
}
707

708
// RightJustify justifies the String to the right by adding padding to the left, up to the
709
// specified length. If the length of the String is already greater than or equal to the specified
710
// length, or the pad is empty, the original String is returned.
711
//
712
// The padding String is repeated as necessary to fill the remaining length.
713
// The padding is added to the left of the String.
714
//
715
// Parameters:
716
//   - length: The desired length of the resulting justified String.
717
//   - pad: The String used as padding.
718
//
719
// Example usage:
720
//
721
//        s := g.String("Hello")
722
//        result := s.RightJustify(10, "...")
723
//        // result: ".....Hello"
724
func (s String) RightJustify(length Int, pad String) String {
4✔
725
        if s.LenRunes() >= length || pad.Empty() {
7✔
726
                return s
3✔
727
        }
3✔
728

729
        output := NewBuilder()
1✔
730

1✔
731
        writePadding(output, pad, pad.LenRunes(), length-s.LenRunes())
1✔
732
        _ = output.Write(s)
1✔
733

1✔
734
        return output.String()
1✔
735
}
736

737
// Center justifies the String by adding padding on both sides, up to the specified length.
738
// If the length of the String is already greater than or equal to the specified length, or the
739
// pad is empty, the original String is returned.
740
//
741
// The padding String is repeated as necessary to evenly distribute the remaining length on both
742
// sides.
743
// The padding is added to the left and right of the String.
744
//
745
// Parameters:
746
//   - length: The desired length of the resulting justified String.
747
//   - pad: The String used as padding.
748
//
749
// Example usage:
750
//
751
//        s := g.String("Hello")
752
//        result := s.Center(10, "...")
753
//        // result: "..Hello..."
754
func (s String) Center(length Int, pad String) String {
4✔
755
        if s.LenRunes() >= length || pad.Empty() {
7✔
756
                return s
3✔
757
        }
3✔
758

759
        output := NewBuilder()
1✔
760

1✔
761
        remains := length - s.LenRunes()
1✔
762

1✔
763
        writePadding(output, pad, pad.LenRunes(), remains/2)
1✔
764
        _ = output.Write(s)
1✔
765
        writePadding(output, pad, pad.LenRunes(), (remains+1)/2)
1✔
766

1✔
767
        return output.String()
1✔
768
}
769

770
// writePadding writes the padding String to the output Builder to fill the remaining length.
771
// It repeats the padding String as necessary and appends any remaining runes from the padding
772
// String.
773
func writePadding(output *Builder, pad String, padlen, remains Int) {
4✔
774
        if repeats := remains / padlen; repeats > 0 {
8✔
775
                _ = output.Write(pad.Repeat(repeats))
4✔
776
        }
4✔
777

778
        padrunes := pad.Runes()
4✔
779
        for i := range remains % padlen {
4✔
780
                _ = output.WriteRune(padrunes[i])
×
781
        }
×
782
}
783

784
// Print writes the content of the String to the standard output (console)
785
// and returns the String unchanged.
NEW
786
func (s String) Print() String { fmt.Print(s); return s }
×
787

788
// Println writes the content of the String to the standard output (console) with a newline
789
// and returns the String unchanged.
NEW
790
func (s String) Println() String { fmt.Println(s); return s }
×
791

792
// trimStringStart returns a slice of the string s, with all leading
793
// white space removed, as defined by Unicode.
794
func trimStringStart(s String) String {
5✔
795
        start := 0
5✔
796

5✔
797
        for ; start < len(s); start++ {
18✔
798
                c := s[start]
13✔
799
                if c >= utf8.RuneSelf {
13✔
800
                        return trimStringFuncStart(s[start:], unicode.IsSpace)
×
801
                }
×
802

803
                if asciiSpace[c] == 0 {
18✔
804
                        break
5✔
805
                }
806
        }
807

808
        return s[start:]
5✔
809
}
810

811
// trimStringEnd returns a slice of the string s, with all trailing
812
// white space removed, as defined by Unicode.
813
func trimStringEnd(s String) String {
5✔
814
        stop := len(s)
5✔
815

5✔
816
        for ; stop > 0; stop-- {
18✔
817
                c := s[stop-1]
13✔
818
                if c >= utf8.RuneSelf {
13✔
819
                        return trimStringFuncEnd(s[:stop], unicode.IsSpace)
×
820
                }
×
821

822
                if asciiSpace[c] == 0 {
18✔
823
                        break
5✔
824
                }
825
        }
826

827
        return s[:stop]
5✔
828
}
829

830
// Helper function to trim leading characters using a unicode function.
831
func trimStringFuncStart(s String, fn func(rune) bool) String {
×
832
        start := 0
×
833

×
834
        for start < len(s) {
×
835
                r, size := utf8.DecodeRuneInString(s[start:].Std())
×
836
                if !fn(r) {
×
837
                        break
×
838
                }
839

840
                start += size
×
841
        }
842

843
        return s[start:]
×
844
}
845

846
// Helper function to trim trailing characters using a unicode function.
847
func trimStringFuncEnd(s String, fn func(rune) bool) String {
×
848
        stop := len(s)
×
849

×
850
        for stop > 0 {
×
851
                r, size := utf8.DecodeLastRuneInString(s[:stop].Std())
×
852
                if !fn(r) {
×
853
                        break
×
854
                }
855

856
                stop -= size
×
857
        }
858

859
        return s[:stop]
×
860
}
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

© 2025 Coveralls, Inc