github.com/enetx/g@v1.0.80/string.go (about)

     1  package g
     2  
     3  import (
     4  	"fmt"
     5  	"regexp"
     6  	"strconv"
     7  	"strings"
     8  	"unicode"
     9  	"unicode/utf8"
    10  
    11  	"github.com/enetx/g/cmp"
    12  	"golang.org/x/text/cases"
    13  	"golang.org/x/text/language"
    14  	"golang.org/x/text/unicode/norm"
    15  )
    16  
    17  // NewString creates a new String from the provided string.
    18  func NewString[T ~string | rune | byte | ~[]rune | ~[]byte](str T) String { return String(str) }
    19  
    20  // Builder returns a new Builder initialized with the content of the String.
    21  func (s String) Builder() *Builder { return NewBuilder().Write(s) }
    22  
    23  // Min returns the minimum of Strings.
    24  func (s String) Min(b ...String) String { return cmp.Min(append(b, s)...) }
    25  
    26  // Max returns the maximum of Strings.
    27  func (s String) Max(b ...String) String { return cmp.Max(append(b, s)...) }
    28  
    29  // Random generates a random String of the specified length, selecting characters from predefined sets.
    30  // If additional character sets are provided, only those will be used; the default set (ASCII_LETTERS and DIGITS)
    31  // is excluded unless explicitly provided.
    32  //
    33  // Parameters:
    34  // - count (Int): Length of the random String to generate.
    35  // - letters (...String): Additional character sets to consider for generating the random String (optional).
    36  //
    37  // Returns:
    38  // - String: Randomly generated String with the specified length.
    39  //
    40  // Example usage:
    41  //
    42  //	randomString := g.String.Random(10)
    43  //	randomString contains a random String with 10 characters.
    44  func (String) Random(count Int, letters ...String) String {
    45  	var chars Slice[rune]
    46  
    47  	if len(letters) != 0 {
    48  		chars = letters[0].ToRunes()
    49  	} else {
    50  		chars = (ASCII_LETTERS + DIGITS).ToRunes()
    51  	}
    52  
    53  	result := NewBuilder()
    54  
    55  	for range count {
    56  		result.WriteRune(chars.Random())
    57  	}
    58  
    59  	return result.String()
    60  }
    61  
    62  // IsASCII checks if all characters in the String are ASCII bytes.
    63  func (s String) IsASCII() bool {
    64  	for _, r := range s {
    65  		if r > unicode.MaxASCII {
    66  			return false
    67  		}
    68  	}
    69  
    70  	return true
    71  }
    72  
    73  // IsDigit checks if all characters in the String are digits.
    74  func (s String) IsDigit() bool {
    75  	if s.Empty() {
    76  		return false
    77  	}
    78  
    79  	for _, c := range s {
    80  		if !unicode.IsDigit(c) {
    81  			return false
    82  		}
    83  	}
    84  
    85  	return true
    86  }
    87  
    88  // ToInt tries to parse the String as an int and returns an Int.
    89  func (s String) ToInt() Result[Int] {
    90  	hint, err := strconv.ParseInt(s.Std(), 0, 32)
    91  	if err != nil {
    92  		return Err[Int](err)
    93  	}
    94  
    95  	return Ok(Int(hint))
    96  }
    97  
    98  // ToFloat tries to parse the String as a float64 and returns an Float.
    99  func (s String) ToFloat() Result[Float] {
   100  	float, err := strconv.ParseFloat(s.Std(), 64)
   101  	if err != nil {
   102  		return Err[Float](err)
   103  	}
   104  
   105  	return Ok(Float(float))
   106  }
   107  
   108  // Title converts the String to title case.
   109  func (s String) Title() String { return String(cases.Title(language.English).String(s.Std())) }
   110  
   111  // Lower returns the String in lowercase.
   112  func (s String) Lower() String { return String(cases.Lower(language.English).String(s.Std())) }
   113  
   114  // Upper returns the String in uppercase.
   115  func (s String) Upper() String { return String(cases.Upper(language.English).String(s.Std())) }
   116  
   117  // Trim trims characters in the cutset from the beginning and end of the String.
   118  func (s String) Trim(cutset String) String {
   119  	return String(strings.Trim(s.Std(), cutset.Std()))
   120  }
   121  
   122  // TrimLeft trims characters in the cutset from the beginning of the String.
   123  func (s String) TrimLeft(cutset String) String {
   124  	return String(strings.TrimLeft(s.Std(), cutset.Std()))
   125  }
   126  
   127  // TrimRight trims characters in the cutset from the end of the String.
   128  func (s String) TrimRight(cutset String) String {
   129  	return String(strings.TrimRight(s.Std(), cutset.Std()))
   130  }
   131  
   132  // TrimPrefix trims the specified prefix from the String.
   133  func (s String) TrimPrefix(prefix String) String {
   134  	return String(strings.TrimPrefix(s.Std(), prefix.Std()))
   135  }
   136  
   137  // TrimSuffix trims the specified suffix from the String.
   138  func (s String) TrimSuffix(suffix String) String {
   139  	return String(strings.TrimSuffix(s.Std(), suffix.Std()))
   140  }
   141  
   142  // Replace replaces the 'oldS' String with the 'newS' String for the specified number of
   143  // occurrences.
   144  func (s String) Replace(oldS, newS String, n Int) String {
   145  	return String(strings.Replace(s.Std(), oldS.Std(), newS.Std(), n.Std()))
   146  }
   147  
   148  // ReplaceAll replaces all occurrences of the 'oldS' String with the 'newS' String.
   149  func (s String) ReplaceAll(oldS, newS String) String {
   150  	return String(strings.ReplaceAll(s.Std(), oldS.Std(), newS.Std()))
   151  }
   152  
   153  // ReplaceMulti creates a custom replacer to perform multiple string replacements.
   154  //
   155  // Parameters:
   156  //
   157  // - oldnew ...String: Pairs of strings to be replaced. Specify as many pairs as needed.
   158  //
   159  // Returns:
   160  //
   161  // - String: A new string with replacements applied using the custom replacer.
   162  //
   163  // Example usage:
   164  //
   165  //	original := g.String("Hello, world! This is a test.")
   166  //	replaced := original.ReplaceMulti(
   167  //	    "Hello", "Greetings",
   168  //	    "world", "universe",
   169  //	    "test", "example",
   170  //	)
   171  //	// replaced contains "Greetings, universe! This is an example."
   172  func (s String) ReplaceMulti(oldnew ...String) String {
   173  	on := Slice[String](oldnew).ToStringSlice()
   174  	return String(strings.NewReplacer(on...).Replace(s.Std()))
   175  }
   176  
   177  // Remove removes all occurrences of specified substrings from the String.
   178  //
   179  // Parameters:
   180  //
   181  // - matches ...String: Substrings to be removed from the string. Specify as many substrings as needed.
   182  //
   183  // Returns:
   184  //
   185  // - String: A new string with all specified substrings removed.
   186  //
   187  // Example usage:
   188  //
   189  //	original := g.String("Hello, world! This is a test.")
   190  //	modified := original.Remove(
   191  //	    "Hello",
   192  //	    "test",
   193  //	)
   194  //	// modified contains ", world! This is a ."
   195  func (s String) Remove(matches ...String) String {
   196  	for _, match := range matches {
   197  		s = s.ReplaceAll(match, "")
   198  	}
   199  
   200  	return s
   201  }
   202  
   203  // ReplaceRegexp replaces all occurrences of the regular expression matches in the String
   204  // with the provided newS (as a String) and returns the resulting String after the replacement.
   205  func (s String) ReplaceRegexp(pattern *regexp.Regexp, newS String) String {
   206  	return String(pattern.ReplaceAllString(s.Std(), newS.Std()))
   207  }
   208  
   209  // FindRegexp searches the String for the first occurrence of the regulare xpression pattern
   210  // and returns an Option[String] containing the matched substring.
   211  // If no match is found, it returns None.
   212  func (s String) FindRegexp(pattern *regexp.Regexp) Option[String] {
   213  	result := String(pattern.FindString(s.Std()))
   214  	if result.Empty() {
   215  		return None[String]()
   216  	}
   217  
   218  	return Some(result)
   219  }
   220  
   221  // ReplaceNth returns a new String instance with the nth occurrence of oldS
   222  // replaced with newS. If there aren't enough occurrences of oldS, the
   223  // original String is returned. If n is less than -1, the original String
   224  // is also returned. If n is -1, the last occurrence of oldS is replaced with newS.
   225  //
   226  // Returns:
   227  //
   228  // - A new String instance with the nth occurrence of oldS replaced with newS.
   229  //
   230  // Example usage:
   231  //
   232  //	s := g.String("The quick brown dog jumped over the lazy dog.")
   233  //	result := s.ReplaceNth("dog", "fox", 2)
   234  //	fmt.Println(result)
   235  //
   236  // Output: "The quick brown dog jumped over the lazy fox.".
   237  func (s String) ReplaceNth(oldS, newS String, n Int) String {
   238  	if n < -1 || len(oldS) == 0 {
   239  		return s
   240  	}
   241  
   242  	count, i := Int(0), Int(0)
   243  
   244  	for {
   245  		pos := s[i:].Index(oldS)
   246  		if pos == -1 {
   247  			break
   248  		}
   249  
   250  		pos += i
   251  		count++
   252  
   253  		if count == n || (n == -1 && s[pos+oldS.Len():].Index(oldS) == -1) {
   254  			return s[:pos] + newS + s[pos+oldS.Len():]
   255  		}
   256  
   257  		i = pos + oldS.Len()
   258  	}
   259  
   260  	return s
   261  }
   262  
   263  // ContainsRegexp checks if the String contains a match for the specified regular expression pattern.
   264  func (s String) ContainsRegexp(pattern String) Result[bool] {
   265  	return ResultOf(regexp.MatchString(pattern.Std(), s.Std()))
   266  }
   267  
   268  // ContainsRegexpAny checks if the String contains a match for any of the specified regular
   269  // expression patterns.
   270  func (s String) ContainsRegexpAny(patterns ...String) Result[bool] {
   271  	for _, pattern := range patterns {
   272  		if r := s.ContainsRegexp(pattern); r.IsErr() || r.Ok() {
   273  			return r
   274  		}
   275  	}
   276  
   277  	return Ok(false)
   278  }
   279  
   280  // ContainsRegexpAll checks if the String contains a match for all of the specified regular expression patterns.
   281  func (s String) ContainsRegexpAll(patterns ...String) Result[bool] {
   282  	for _, pattern := range patterns {
   283  		if r := s.ContainsRegexp(pattern); r.IsErr() || !r.Ok() {
   284  			return r
   285  		}
   286  	}
   287  
   288  	return Ok(true)
   289  }
   290  
   291  // Contains checks if the String contains the specified substring.
   292  func (s String) Contains(substr String) bool { return strings.Contains(s.Std(), substr.Std()) }
   293  
   294  // ContainsAny checks if the String contains any of the specified substrings.
   295  func (s String) ContainsAny(substrs ...String) bool {
   296  	for _, substr := range substrs {
   297  		if s.Contains(substr) {
   298  			return true
   299  		}
   300  	}
   301  
   302  	return false
   303  }
   304  
   305  // ContainsAll checks if the given String contains all the specified substrings.
   306  func (s String) ContainsAll(substrs ...String) bool {
   307  	for _, substr := range substrs {
   308  		if !s.Contains(substr) {
   309  			return false
   310  		}
   311  	}
   312  
   313  	return true
   314  }
   315  
   316  // ContainsAnyChars checks if the String contains any characters from the specified String.
   317  func (s String) ContainsAnyChars(chars String) bool {
   318  	return strings.ContainsAny(s.Std(), chars.Std())
   319  }
   320  
   321  // StartsWith checks if the String starts with any of the provided prefixes.
   322  // The method accepts a variable number of arguments, allowing for checking against multiple
   323  // prefixes at once. It iterates over the provided prefixes and uses the HasPrefix function from
   324  // the strings package to check if the String starts with each prefix.
   325  // The function returns true if the String starts with any of the prefixes, and false otherwise.
   326  //
   327  // Example usage:
   328  //
   329  //	s := g.String("http://example.com")
   330  //	if s.StartsWith("http://", "https://") {
   331  //	   // do something
   332  //	}
   333  func (s String) StartsWith(prefixes ...String) bool {
   334  	for _, prefix := range prefixes {
   335  		if strings.HasPrefix(string(s), prefix.Std()) {
   336  			return true
   337  		}
   338  	}
   339  
   340  	return false
   341  }
   342  
   343  // EndsWith checks if the String ends with any of the provided suffixes.
   344  // The method accepts a variable number of arguments, allowing for checking against multiple
   345  // suffixes at once. It iterates over the provided suffixes and uses the HasSuffix function from
   346  // the strings package to check if the String ends with each suffix.
   347  // The function returns true if the String ends with any of the suffixes, and false otherwise.
   348  //
   349  // Example usage:
   350  //
   351  //	s := g.String("example.com")
   352  //	if s.EndsWith(".com", ".net") {
   353  //	   // do something
   354  //	}
   355  func (s String) EndsWith(suffixes ...String) bool {
   356  	for _, suffix := range suffixes {
   357  		if strings.HasSuffix(string(s), suffix.Std()) {
   358  			return true
   359  		}
   360  	}
   361  
   362  	return false
   363  }
   364  
   365  // Lines splits the String by lines and returns the iterator.
   366  func (s String) Lines() SeqSlice[String] { return linesString(s) }
   367  
   368  // Fields splits the String into a slice of substrings, removing any whitespace, and returns the iterator.
   369  func (s String) Fields() SeqSlice[String] { return fieldsString(s) }
   370  
   371  // FieldsBy splits the String into a slice of substrings using a custom function to determine the field boundaries,
   372  // and returns the iterator.
   373  func (s String) FieldsBy(fn func(r rune) bool) SeqSlice[String] { return fieldsbyString(s, fn) }
   374  
   375  // Split splits the String by the specified separator and returns the iterator.
   376  func (s String) Split(sep ...String) SeqSlice[String] {
   377  	var separator String
   378  	if len(sep) != 0 {
   379  		separator = sep[0]
   380  	}
   381  
   382  	return splitString(s, separator, 0)
   383  }
   384  
   385  // SplitAfter splits the String after each instance of the specified separator and returns the iterator.
   386  func (s String) SplitAfter(sep String) SeqSlice[String] { return splitString(s, sep, sep.Len()) }
   387  
   388  // SplitN splits the String into substrings using the provided separator and returns an Slice[String] of the results.
   389  // The n parameter controls the number of substrings to return:
   390  // - If n is negative, there is no limit on the number of substrings returned.
   391  // - If n is zero, an empty Slice[String] is returned.
   392  // - If n is positive, at most n substrings are returned.
   393  func (s String) SplitN(sep String, n Int) Slice[String] {
   394  	return SliceMap(strings.SplitN(s.Std(), sep.Std(), n.Std()), NewString)
   395  }
   396  
   397  // SplitRegexp splits the String into substrings using the provided regular expression pattern and returns an Slice[String] of the results.
   398  // The regular expression pattern is provided as a regexp.Regexp parameter.
   399  func (s String) SplitRegexp(pattern regexp.Regexp) Slice[String] {
   400  	return SliceMap(pattern.Split(s.Std(), -1), NewString)
   401  }
   402  
   403  // SplitRegexpN splits the String into substrings using the provided regular expression pattern and returns an Slice[String] of the results.
   404  // The regular expression pattern is provided as a regexp.Regexp parameter.
   405  // The n parameter controls the number of substrings to return:
   406  // - If n is negative, there is no limit on the number of substrings returned.
   407  // - If n is zero, an empty Slice[String] is returned.
   408  // - If n is positive, at most n substrings are returned.
   409  func (s String) SplitRegexpN(pattern regexp.Regexp, n Int) Option[Slice[String]] {
   410  	result := SliceMap(pattern.Split(s.Std(), n.Std()), NewString)
   411  	if result.Empty() {
   412  		return None[Slice[String]]()
   413  	}
   414  
   415  	return Some(result)
   416  }
   417  
   418  // Chunks splits the String into chunks of the specified size.
   419  //
   420  // This function iterates through the String, creating new String chunks of the specified size.
   421  // If size is less than or equal to 0 or the String is empty,
   422  // it returns an empty Slice[String].
   423  // If size is greater than or equal to the length of the String,
   424  // it returns an Slice[String] containing the original String.
   425  //
   426  // Parameters:
   427  //
   428  // - size (Int): The size of the chunks to split the String into.
   429  //
   430  // Returns:
   431  //
   432  // - Slice[String]: A slice of String chunks of the specified size.
   433  //
   434  // Example usage:
   435  //
   436  //	text := g.String("Hello, World!")
   437  //	chunks := text.Chunks(4)
   438  //
   439  // chunks contains {"Hell", "o, W", "orld", "!"}.
   440  func (s String) Chunks(size Int) Slice[String] {
   441  	if size <= 0 || s.Empty() {
   442  		return nil
   443  	}
   444  
   445  	if size >= s.Len() {
   446  		return Slice[String]{s}
   447  	}
   448  
   449  	return SliceMap(s.Split().Chunks(size).Collect(), func(ch Slice[String]) String { return ch.Join() })
   450  }
   451  
   452  // Cut returns two String values. The first String contains the remainder of the
   453  // original String after the cut. The second String contains the text between the
   454  // first occurrences of the 'start' and 'end' strings, with tags removed if specified.
   455  //
   456  // The function searches for the 'start' and 'end' strings within the String.
   457  // If both are found, it returns the first String containing the remainder of the
   458  // original String after the cut, followed by the second String containing the text
   459  // between the first occurrences of 'start' and 'end' with tags removed if specified.
   460  //
   461  // If either 'start' or 'end' is empty or not found in the String, it returns the
   462  // original String as the second String, and an empty String as the first.
   463  //
   464  // Parameters:
   465  //
   466  // - start (String): The String marking the beginning of the text to be cut.
   467  //
   468  // - end (String): The String marking the end of the text to be cut.
   469  //
   470  //   - rmtags (bool, optional): An optional boolean parameter indicating whether
   471  //     to remove 'start' and 'end' tags from the cut text. Defaults to false.
   472  //
   473  // Returns:
   474  //
   475  //   - String: The first String containing the remainder of the original String
   476  //     after the cut, with tags removed if specified,
   477  //     or an empty String if 'start' or 'end' is empty or not found.
   478  //
   479  //   - String: The second String containing the text between the first occurrences of
   480  //     'start' and 'end', or the original String if 'start' or 'end' is empty or not found.
   481  //
   482  // Example usage:
   483  //
   484  //	s := g.String("Hello, [world]! How are you?")
   485  //	remainder, cut := s.Cut("[", "]")
   486  //	// remainder: "Hello, ! How are you?"
   487  //	// cut: "world"
   488  func (s String) Cut(start, end String, rmtags ...bool) (String, String) {
   489  	if start.Empty() || end.Empty() {
   490  		return s, ""
   491  	}
   492  
   493  	startIndex := s.Index(start)
   494  	if startIndex == -1 {
   495  		return s, ""
   496  	}
   497  
   498  	endIndex := s[startIndex+start.Len():].Index(end)
   499  	if endIndex == -1 {
   500  		return s, ""
   501  	}
   502  
   503  	cut := s[startIndex+start.Len() : startIndex+start.Len()+endIndex]
   504  
   505  	startCutIndex := startIndex
   506  	endCutIndex := startIndex + start.Len() + endIndex
   507  
   508  	if len(rmtags) != 0 && !rmtags[0] {
   509  		startCutIndex += start.Len()
   510  	} else {
   511  		endCutIndex += end.Len()
   512  	}
   513  
   514  	remainder := s[:startCutIndex] + s[endCutIndex:]
   515  
   516  	return remainder, cut
   517  }
   518  
   519  // Similarity calculates the similarity between two Strings using the
   520  // Levenshtein distance algorithm and returns the similarity percentage as an Float.
   521  //
   522  // The function compares two Strings using the Levenshtein distance,
   523  // which measures the difference between two sequences by counting the number
   524  // of single-character edits required to change one sequence into the other.
   525  // The similarity is then calculated by normalizing the distance by the maximum
   526  // length of the two input Strings.
   527  //
   528  // Parameters:
   529  //
   530  // - str (String): The String to compare with s.
   531  //
   532  // Returns:
   533  //
   534  // - Float: The similarity percentage between the two Strings as a value between 0 and 100.
   535  //
   536  // Example usage:
   537  //
   538  //	s1 := g.String("kitten")
   539  //	s2 := g.String("sitting")
   540  //	similarity := s1.Similarity(s2) // 57.14285714285714
   541  func (s String) Similarity(str String) Float {
   542  	if s.Eq(str) {
   543  		return 100
   544  	}
   545  
   546  	if s.Empty() || str.Empty() {
   547  		return 0
   548  	}
   549  
   550  	s1 := s.ToRunes()
   551  	s2 := str.ToRunes()
   552  
   553  	lenS1 := s.LenRunes()
   554  	lenS2 := str.LenRunes()
   555  
   556  	if lenS1 > lenS2 {
   557  		s1, s2, lenS1, lenS2 = s2, s1, lenS2, lenS1
   558  	}
   559  
   560  	distance := NewSlice[Int](lenS1 + 1)
   561  
   562  	for i, r2 := range s2 {
   563  		prev := Int(i) + 1
   564  
   565  		for j, r1 := range s1 {
   566  			current := distance[j]
   567  			if r2 != r1 {
   568  				current = distance[j].Add(1).Min(prev + 1).Min(distance[j+1] + 1)
   569  			}
   570  
   571  			distance[j], prev = prev, current
   572  		}
   573  
   574  		distance[lenS1] = prev
   575  	}
   576  
   577  	return Float(1).Sub(distance[lenS1].ToFloat() / lenS1.Max(lenS2).ToFloat()).Mul(100)
   578  }
   579  
   580  // Cmp compares two Strings and returns an cmp.Ordering indicating their relative order.
   581  // The result will be cmp.Equal if s==str, cmp.Less if s < str, and cmp.Greater if s > str.
   582  func (s String) Cmp(str String) cmp.Ordering { return cmp.Cmp(s, str) }
   583  
   584  // Append appends the specified String to the current String.
   585  func (s String) Append(str String) String { return s + str }
   586  
   587  // Prepend prepends the specified String to the current String.
   588  func (s String) Prepend(str String) String { return str + s }
   589  
   590  // ContainsRune checks if the String contains the specified rune.
   591  func (s String) ContainsRune(r rune) bool { return strings.ContainsRune(s.Std(), r) }
   592  
   593  // Count returns the number of non-overlapping instances of the substring in the String.
   594  func (s String) Count(substr String) Int { return Int(strings.Count(s.Std(), substr.Std())) }
   595  
   596  // Empty checks if the String is empty.
   597  func (s String) Empty() bool { return len(s) == 0 }
   598  
   599  // Eq checks if two Strings are equal.
   600  func (s String) Eq(str String) bool { return s == str }
   601  
   602  // EqFold compares two String strings case-insensitively.
   603  func (s String) EqFold(str String) bool { return strings.EqualFold(s.Std(), str.Std()) }
   604  
   605  // Gt checks if the String is greater than the specified String.
   606  func (s String) Gt(str String) bool { return s > str }
   607  
   608  // Gte checks if the String is greater than or equal to the specified String.
   609  func (s String) Gte(str String) bool { return s >= str }
   610  
   611  // Bytes returns the String as an Bytes.
   612  func (s String) ToBytes() Bytes { return Bytes(s) }
   613  
   614  // Index returns the index of the first instance of the specified substring in the String, or -1
   615  // if substr is not present in s.
   616  func (s String) Index(substr String) Int { return Int(strings.Index(s.Std(), substr.Std())) }
   617  
   618  // IndexRegexp searches for the first occurrence of the regular expression pattern in the String.
   619  // If a match is found, it returns an Option containing an Slice with the start and end indices of the match.
   620  // If no match is found, it returns None.
   621  func (s String) IndexRegexp(pattern *regexp.Regexp) Option[Slice[Int]] {
   622  	result := SliceMap(pattern.FindStringIndex(s.Std()), NewInt)
   623  	if result.Empty() {
   624  		return None[Slice[Int]]()
   625  	}
   626  
   627  	return Some(result)
   628  }
   629  
   630  // FindAllRegexp searches the String for all occurrences of the regular expression pattern
   631  // and returns an Option[Slice[String]] containing a slice of matched substrings.
   632  // If no matches are found, the Option[Slice[String]] will be None.
   633  func (s String) FindAllRegexp(pattern *regexp.Regexp) Option[Slice[String]] {
   634  	return s.FindAllRegexpN(pattern, -1)
   635  }
   636  
   637  // FindAllRegexpN searches the String for up to n occurrences of the regular expression pattern
   638  // and returns an Option[Slice[String]] containing a slice of matched substrings.
   639  // If no matches are found, the Option[Slice[String]] will be None.
   640  // If n is negative, all occurrences will be returned.
   641  func (s String) FindAllRegexpN(pattern *regexp.Regexp, n Int) Option[Slice[String]] {
   642  	result := SliceMap(pattern.FindAllString(s.Std(), n.Std()), NewString)
   643  	if result.Empty() {
   644  		return None[Slice[String]]()
   645  	}
   646  
   647  	return Some(result)
   648  }
   649  
   650  // FindSubmatchRegexp searches the String for the first occurrence of the regular expression pattern
   651  // and returns an Option[Slice[String]] containing the matched substrings and submatches.
   652  // The Option will contain an Slice[String] with the full match at index 0, followed by any captured submatches.
   653  // If no match is found, it returns None.
   654  func (s String) FindSubmatchRegexp(pattern *regexp.Regexp) Option[Slice[String]] {
   655  	result := SliceMap(pattern.FindStringSubmatch(s.Std()), NewString)
   656  	if result.Empty() {
   657  		return None[Slice[String]]()
   658  	}
   659  
   660  	return Some(result)
   661  }
   662  
   663  // FindAllSubmatchRegexp searches the String for all occurrences of the regular expression pattern
   664  // and returns an Option[Slice[Slice[String]]] containing the matched substrings and submatches.
   665  // The Option[Slice[Slice[String]]] will contain an Slice[String] for each match,
   666  // where each Slice[String] will contain the full match at index 0, followed by any captured submatches.
   667  // If no match is found, the Option[Slice[Slice[String]]] will be None.
   668  // This method is equivalent to calling SubmatchAllRegexpN with n = -1, which means it finds all occurrences.
   669  func (s String) FindAllSubmatchRegexp(pattern *regexp.Regexp) Option[Slice[Slice[String]]] {
   670  	return s.FindAllSubmatchRegexpN(pattern, -1)
   671  }
   672  
   673  // FindAllSubmatchRegexpN searches the String for occurrences of the regular expression pattern
   674  // and returns an Option[Slice[Slice[String]]] containing the matched substrings and submatches.
   675  // The Option[Slice[Slice[String]]] will contain an Slice[String] for each match,
   676  // where each Slice[String] will contain the full match at index 0, followed by any captured submatches.
   677  // If no match is found, the Option[Slice[Slice[String]]] will be None.
   678  // The 'n' parameter specifies the maximum number of matches to find. If n is negative, it finds all occurrences.
   679  func (s String) FindAllSubmatchRegexpN(pattern *regexp.Regexp, n Int) Option[Slice[Slice[String]]] {
   680  	var result Slice[Slice[String]]
   681  
   682  	for _, v := range pattern.FindAllStringSubmatch(s.Std(), n.Std()) {
   683  		result = append(result, SliceMap(v, NewString))
   684  	}
   685  
   686  	if result.Empty() {
   687  		return None[Slice[Slice[String]]]()
   688  	}
   689  
   690  	return Some(result)
   691  }
   692  
   693  // LastIndex returns the index of the last instance of the specified substring in the String, or -1
   694  // if substr is not present in s.
   695  func (s String) LastIndex(substr String) Int { return Int(strings.LastIndex(s.Std(), substr.Std())) }
   696  
   697  // IndexRune returns the index of the first instance of the specified rune in the String.
   698  func (s String) IndexRune(r rune) Int { return Int(strings.IndexRune(s.Std(), r)) }
   699  
   700  // Len returns the length of the String.
   701  func (s String) Len() Int { return Int(len(s)) }
   702  
   703  // LenRunes returns the number of runes in the String.
   704  func (s String) LenRunes() Int { return Int(utf8.RuneCountInString(s.Std())) }
   705  
   706  // Lt checks if the String is less than the specified String.
   707  func (s String) Lt(str String) bool { return s < str }
   708  
   709  // Lte checks if the String is less than or equal to the specified String.
   710  func (s String) Lte(str String) bool { return s <= str }
   711  
   712  // Map applies the provided function to all runes in the String and returns the resulting String.
   713  func (s String) Map(fn func(rune) rune) String { return String(strings.Map(fn, s.Std())) }
   714  
   715  // NormalizeNFC returns a new String with its Unicode characters normalized using the NFC form.
   716  func (s String) NormalizeNFC() String { return String(norm.NFC.String(s.Std())) }
   717  
   718  // Ne checks if two Strings are not equal.
   719  func (s String) Ne(str String) bool { return !s.Eq(str) }
   720  
   721  // NotEmpty checks if the String is not empty.
   722  func (s String) NotEmpty() bool { return s.Len() != 0 }
   723  
   724  // Reader returns a *strings.Reader initialized with the content of String.
   725  func (s String) Reader() *strings.Reader { return strings.NewReader(s.Std()) }
   726  
   727  // Repeat returns a new String consisting of the specified count of the original String.
   728  func (s String) Repeat(count Int) String { return String(strings.Repeat(s.Std(), count.Std())) }
   729  
   730  // Reverse reverses the String.
   731  func (s String) Reverse() String { return s.ToBytes().Reverse().ToString() }
   732  
   733  // ToRunes returns the String as a slice of runes.
   734  func (s String) ToRunes() Slice[rune] { return []rune(s) }
   735  
   736  // Chars splits the String into individual characters and returns the iterator.
   737  func (s String) Chars() SeqSlice[String] { return s.Split() }
   738  
   739  // Std returns the String as a string.
   740  func (s String) Std() string { return string(s) }
   741  
   742  // TrimSpace trims whitespace from the beginning and end of the String.
   743  func (s String) TrimSpace() String { return String(strings.TrimSpace(s.Std())) }
   744  
   745  // Format applies a specified format to the String object.
   746  func (s String) Format(format String) String { return Sprintf(format, s) }
   747  
   748  // LeftJustify justifies the String to the left by adding padding to the right, up to the
   749  // specified length. If the length of the String is already greater than or equal to the specified
   750  // length, or the pad is empty, the original String is returned.
   751  //
   752  // The padding String is repeated as necessary to fill the remaining length.
   753  // The padding is added to the right of the String.
   754  //
   755  // Parameters:
   756  //   - length: The desired length of the resulting justified String.
   757  //   - pad: The String used as padding.
   758  //
   759  // Example usage:
   760  //
   761  //	s := g.String("Hello")
   762  //	result := s.LeftJustify(10, "...")
   763  //	// result: "Hello....."
   764  func (s String) LeftJustify(length Int, pad String) String {
   765  	if s.LenRunes() >= length || pad.Eq("") {
   766  		return s
   767  	}
   768  
   769  	output := NewBuilder()
   770  
   771  	_ = output.Write(s)
   772  	writePadding(output, pad, pad.LenRunes(), length-s.LenRunes())
   773  
   774  	return output.String()
   775  }
   776  
   777  // RightJustify justifies the String to the right by adding padding to the left, up to the
   778  // specified length. If the length of the String is already greater than or equal to the specified
   779  // length, or the pad is empty, the original String is returned.
   780  //
   781  // The padding String is repeated as necessary to fill the remaining length.
   782  // The padding is added to the left of the String.
   783  //
   784  // Parameters:
   785  //   - length: The desired length of the resulting justified String.
   786  //   - pad: The String used as padding.
   787  //
   788  // Example usage:
   789  //
   790  //	s := g.String("Hello")
   791  //	result := s.RightJustify(10, "...")
   792  //	// result: ".....Hello"
   793  func (s String) RightJustify(length Int, pad String) String {
   794  	if s.LenRunes() >= length || pad.Empty() {
   795  		return s
   796  	}
   797  
   798  	output := NewBuilder()
   799  
   800  	writePadding(output, pad, pad.LenRunes(), length-s.LenRunes())
   801  	_ = output.Write(s)
   802  
   803  	return output.String()
   804  }
   805  
   806  // Center justifies the String by adding padding on both sides, up to the specified length.
   807  // If the length of the String is already greater than or equal to the specified length, or the
   808  // pad is empty, the original String is returned.
   809  //
   810  // The padding String is repeated as necessary to evenly distribute the remaining length on both
   811  // sides.
   812  // The padding is added to the left and right of the String.
   813  //
   814  // Parameters:
   815  //   - length: The desired length of the resulting justified String.
   816  //   - pad: The String used as padding.
   817  //
   818  // Example usage:
   819  //
   820  //	s := g.String("Hello")
   821  //	result := s.Center(10, "...")
   822  //	// result: "..Hello..."
   823  func (s String) Center(length Int, pad String) String {
   824  	if s.LenRunes() >= length || pad.Empty() {
   825  		return s
   826  	}
   827  
   828  	output := NewBuilder()
   829  
   830  	remains := length - s.LenRunes()
   831  
   832  	writePadding(output, pad, pad.LenRunes(), remains/2)
   833  	_ = output.Write(s)
   834  	writePadding(output, pad, pad.LenRunes(), (remains+1)/2)
   835  
   836  	return output.String()
   837  }
   838  
   839  // writePadding writes the padding String to the output Builder to fill the remaining length.
   840  // It repeats the padding String as necessary and appends any remaining runes from the padding
   841  // String.
   842  func writePadding(output *Builder, pad String, padlen, remains Int) {
   843  	if repeats := remains / padlen; repeats > 0 {
   844  		_ = output.Write(pad.Repeat(repeats))
   845  	}
   846  
   847  	padrunes := pad.ToRunes()
   848  	for i := range remains % padlen {
   849  		_ = output.WriteRune(padrunes[i])
   850  	}
   851  }
   852  
   853  // Print prints the content of the String to the standard output (console)
   854  // and returns the String unchanged.
   855  func (s String) Print() String { fmt.Println(s); return s }