Skip to content

Releases: goforj/str

v3.0.0

Choose a tag to compare

@cmilesio cmilesio released this 12 Sep 02:51
bbaa405

Version 3 aligns the fluent API with all 56 non-constructor functions in Go 1.27's strings package while keeping Go 1.24 as the minimum and adding no runtime dependencies.

Install

go get github.com/goforj/str/v3@v3.0.0
import "github.com/goforj/str/v3"

Breaking changes and migration

Changing the import path alone is not enough. Review the v2 to v3 migration guide for the complete behavior changes.

Trimming and joining

v2 v3
value.Trim() value.TrimSpace()
value.TrimChars(chars) value.Trim(chars)
value.TrimLeft() value.TrimLeftFunc(unicode.IsSpace)
value.TrimRight() value.TrimRightFunc(unicode.IsSpace)
value.Join(elements, sep) str.Join(elements, sep)

Trim, TrimLeft, and TrimRight accept cutsets. An empty cutset leaves the input unchanged. TrimChars and the receiver method Join are removed.

Search indexes and empty matches

Index and LastIndex now return byte offsets. All standard index methods follow that rule; application helpers such as Slice, Take, and CharAt still use rune positions.

// v2: Index("llo") returned 2.
value := str.Of("héllo")
index := value.Index("llo")
fmt.Println(index)
// 3
fmt.Println(value.String()[index:])
// llo

Empty searches follow each standard function's contract. For example, Contains("") is true, Count("") on "abc" is 4, and ReplaceAll("", "-") produces "-a-b-c-".

Folded predicates also match empty searches. Empty-search replacement helpers insert at the corresponding boundaries. Replace, ReplaceAll, ReplaceFold, and each entry in ReplaceArray preserve UTF-8 sequence boundaries; Swap follows strings.Replacer byte boundaries, so an empty key can split a multibyte rune. Swap remains one-pass; ReplaceArray remains sequential.

Lines, casing, and repetition

Lines now returns iter.Seq[string], preserves newline bytes, and omits an extra empty element after a trailing newline.

// v2: Lines() returned ["a" "b" ""].
fmt.Printf("%q\n", slices.Collect(str.Of("a\r\nb\n").Lines()))
// ["a\r\n" "b\n"]

Use NormalizeNewlines().Split("\n") for the old normalized slice behavior. Lines, SplitSeq, and SplitAfterSeq are single-use; FieldsSeq and FieldsFuncSeq restart on each iteration.

Title now preserves non-initial letters like strings.Title: "hELLO wORLD" becomes "HELLO WORLD", where v2 produced "Hello World". It carries the standard deprecation notice for Unicode word-boundary limitations. Repeat now panics for negative counts and overflow.

Additional standard operations

The API adds the Cut family, fields and split variants, predicate-based search and trimming, Replace(old, new, n), Map, Clone, ToValidUTF8, special-case conversion, and the remaining standard string functions. See the complete API contract.

The source string becomes the receiver, remaining arguments retain their order, and a single string result becomes str.String. Multiple results, slices, and iterators retain their standard types. Join is a package-level constructor. Stateful strings.Builder, strings.Reader, and strings.Replacer remain in the standard library.

CutLast is backported so it also works on Go 1.24. The docs and examples modules are development tooling; only the root runtime module is released. Existing v2 consumers can continue using the v2 module path.

Full changelog: v2.0.1 to v3.0.0.

v2.0.1

Choose a tag to compare

@cmilesio cmilesio released this 13 Jul 00:46

Full Changelog: v2.0.0...v2.0.1

v2.0.0

Choose a tag to compare

@cmilesio cmilesio released this 13 Jul 00:25

v2 makes str smaller and more predictable without changing the reason it exists: readable string chains.

The API now has one name for each operation, follows Go's standard library where there is an established name, and handles Unicode consistently. Fluent operations such as Join, ToBase64, and FromBase64 remain part of the library.

Highlights

  • Standard library naming such as Trim, HasPrefix, HasSuffix, EqualFold, TrimPrefix, and TrimSuffix.
  • No aliases or compatibility shims. Each operation has one supported spelling.
  • Rune-aware indexing, slicing, padding, casing, and text limits.
  • Better word detection for camel case and acronyms such as HTTPRequestID.
  • Clear empty-search behavior and errors for invalid parsing or match patterns.
  • Improved Plural, Singular, Slug, Words, and WrapWords behavior for application code.
  • A generated API reference with examples that are built, run, and checked by the test suite.

Breaking changes

The module path is now:

import "github.com/goforj/str/v2"

Aliases and several narrow wrappers were removed, and some methods now use Go-style names or more explicit signatures. See the v1 to v2 migration guide for direct replacements and behavior changes.

Install

go get github.com/goforj/str/v2@v2.0.0

v2 requires Go 1.24 or newer and has no third-party dependencies.

Full Changelog: v1.3.0...v2.0.0

v1.3.0

Choose a tag to compare

@cmilesio cmilesio released this 10 Mar 23:59

This release expands str with a small set of high-leverage additions:

  • Case-insensitive search helpers now include positional and counting APIs.
  • New character-class predicates cover common validation checks.
  • Word and affix helpers fill a few practical gaps without expanding the surface area.
  • Native-type conversions were added in a deliberately minimal form: Int, Float64, and Bool.

str aims to be intentionally simple by providing sane defaults, no aliased helpers and high-value primitives.

Search

IndexFold()

Returns the rune index of the first case-insensitive match.

v := str.Of("Go gopher GO").IndexFold("go")
println(v)
// 0

LastIndexFold()

Returns the rune index of the last case-insensitive match.

v := str.Of("Go gopher GO").LastIndexFold("go")
println(v)
// 10

CountFold()

Counts non-overlapping case-insensitive matches.

v := str.Of("GoGOgophergo").CountFold("go")
println(v)
// 4

Checks

IsAlpha()

Reports whether a string consists only of Unicode letters.

v := str.Of("Gopher").IsAlpha()
println(v)
// true

IsAlnum()

Reports whether a string consists only of Unicode letters or numbers.

v := str.Of("Gopher2025").IsAlnum()
println(v)
// true

IsNumeric()

Reports whether a string consists only of Unicode numbers.

v := str.Of("12345").IsNumeric()
println(v)
// true

Words

Initials()

Extracts the uppercase first rune of each detected word.

v := str.Of("portableNetwork graphics").Initials().String()
println(v)
// PNG

Substrings

BeforeFold()

Returns the substring before the first case-insensitive separator match.

v := str.Of("GoPHER::go").BeforeFold("::GO").String()
println(v)
// GoPHER

AfterFold()

Returns the substring after the first case-insensitive separator match.

v := str.Of("gopher::GO-team").AfterFold("::go").String()
println(v)
// -team

BeforeLastFold()

Returns the substring before the last case-insensitive separator match.

v := str.Of("pkg/Path/FILE.txt").BeforeLastFold("/path/").String()
println(v)
// pkg

AfterLastFold()

Returns the substring after the last case-insensitive separator match.

v := str.Of("pkg/Path/FILE.txt").AfterLastFold("/path/").String()
println(v)
// FILE.txt

CommonPrefix()

Returns the longest shared prefix across the receiver and the provided strings.

v := str.Of("gopher").CommonPrefix("go", "gold").String()
println(v)
// go

CommonSuffix()

Returns the longest shared suffix across the receiver and the provided strings.

v := str.Of("main_test.go").CommonSuffix("user_test.go", "api_test.go").String()
println(v)
// _test.go

Affixes

HasSurrounding()

Reports whether a string starts and ends with the expected delimiters.

v := str.Of(`"GoForj"`).HasSurrounding(`"`, "")
println(v)
// true

Conversion

Int()

Parses the string as a base-10 int using strict strconv.Atoi semantics.

v, err := str.Of("42").Int()
println(v, err == nil)
// 42 true

Float64()

Parses the string as a float64 using strict strconv.ParseFloat semantics.

v, err := str.Of("3.14").Float64()
println(v, err == nil)
// 3.14 true

Bool()

Parses the string as a bool using strict strconv.ParseBool semantics.

v, err := str.Of("true").Bool()
println(v, err == nil)
// true true

Full Changelog: v1.2.0...v1.3.0

v1.2.0

Choose a tag to compare

@cmilesio cmilesio released this 28 Dec 19:20

Changes

  • feat: add TrimSpace as wrapper to Trim("") @cmilesdev
  • test: align test filenames with source files @cmilesdev

Full Changelog: v1.1.2...v1.2.0

v1.1.2

Choose a tag to compare

@cmilesio cmilesio released this 28 Dec 06:12

Full Changelog: v1.1.1...v1.1.2

v1.1.1

Choose a tag to compare

@cmilesio cmilesio released this 28 Dec 03:01

Full Changelog: v1.1.0...v1.1.1

v1.1.0

Choose a tag to compare

@cmilesio cmilesio released this 28 Dec 00:59

Full Changelog: v1.0.0...v1.1.0

v1.0.0

Choose a tag to compare

@cmilesio cmilesio released this 18 Dec 02:44