Skip to content

Commit

Permalink
Merge pull request #2 from RaduBerinde/crstrings
Browse files Browse the repository at this point in the history
add crstrings library
  • Loading branch information
RaduBerinde authored Sep 4, 2024
2 parents ae293ba + 8212df1 commit 3474b32
Show file tree
Hide file tree
Showing 2 changed files with 182 additions and 0 deletions.
99 changes: 99 additions & 0 deletions crstrings/utils.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// Copyright 2024 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

package crstrings

import (
"fmt"
"slices"
"strings"
)

// JoinStringers concatenates the string representations of the given
// fmt.Stringer implementations.
func JoinStringers[T fmt.Stringer](delim string, args ...T) string {
switch len(args) {
case 0:
return ""
case 1:
return args[0].String()
}
elems := make([]string, len(args))
for i := range args {
elems[i] = args[i].String()
}
return strings.Join(elems, delim)
}

// MapAndJoin converts each argument to a string using the given function and
// joins the strings with the given delimiter.
func MapAndJoin[T any](fn func(T) string, delim string, args ...T) string {
switch len(args) {
case 0:
return ""
case 1:
return fn(args[0])
}
elems := make([]string, len(args))
for i := range args {
elems[i] = fn(args[i])
}
return strings.Join(elems, delim)
}

// If returns the given value if the flag is true, otherwise an empty string.
func If(flag bool, trueValue string) string {
return IfElse(flag, trueValue, "")
}

// IfElse returns the value that matches the value of the flag.
func IfElse(flag bool, trueValue, falseValue string) string {
if flag {
return trueValue
}
return falseValue
}

// WithSep prints the strings a and b with the given separator in-between,
// unless one of the strings is empty (in which case the other string is
// returned).
func WithSep(a string, separator string, b string) string {
if a == "" {
return b
}
if b == "" {
return a
}
return strings.Join([]string{a, b}, separator)
}

// FilterEmpty removes empty strings from the given slice.
func FilterEmpty(elems []string) []string {
return slices.DeleteFunc(elems, func(s string) bool {
return s == ""
})
}

// Lines breaks up the given string into lines.
func Lines(s string) []string {
// Remove any trailing newline (to avoid getting an extraneous empty line at
// the end).
s = strings.TrimSuffix(s, "\n")
if s == "" {
// In this case, Split returns a slice with a single empty string (which is
// not what we want).
return nil
}
return strings.Split(s, "\n")
}
83 changes: 83 additions & 0 deletions crstrings/utils_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// Copyright 2024 The Cockroach Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
// implied. See the License for the specific language governing
// permissions and limitations under the License.

package crstrings

import (
"fmt"
"strings"
"testing"
)

type num int

func (n num) String() string {
return fmt.Sprintf("%03d", int(n))
}

func TestJoinStringers(t *testing.T) {
nums := []num{0, 1, 2, 3}
expect(t, "", JoinStringers(", ", nums[:0]...))
expect(t, "000", JoinStringers(", ", nums[0]))
expect(t, "000, 001", JoinStringers(", ", nums[0], nums[1]))
expect(t, "000, 001, 002, 003", JoinStringers(", ", nums...))
}

func TestMapAndJoin(t *testing.T) {
nums := []int{0, 1, 2, 3}
fn := func(n int) string {
return fmt.Sprintf("%d", n)
}
expect(t, "", MapAndJoin(fn, ", ", nums[:0]...))
expect(t, "0", MapAndJoin(fn, ", ", nums[0]))
expect(t, "0, 1", MapAndJoin(fn, ", ", nums[0], nums[1]))
expect(t, "0, 1, 2, 3", MapAndJoin(fn, ", ", nums...))
}

func expect(t *testing.T, expected, actual string) {
t.Helper()
if actual != expected {
t.Errorf("expected %q got %q", expected, actual)
}
}

func TestIf(t *testing.T) {
expect(t, "", If(false, "true"))
expect(t, "true", If(true, "true"))
}

func TestIfElse(t *testing.T) {
expect(t, "false", IfElse(false, "true", "false"))
expect(t, "true", IfElse(true, "true", "false"))
}

func TestWithSep(t *testing.T) {
expect(t, "a,b", WithSep("a", ",", "b"))
expect(t, "a", WithSep("a", ",", ""))
expect(t, "b", WithSep("", ",", "b"))
}

func TestFilterEmpty(t *testing.T) {
s := []string{"a", "", "b", "", "c", ""}
expect(t, "a,b,c", strings.Join(FilterEmpty(s), ","))
}

func TestLines(t *testing.T) {
expect(t, `["a" "b" "c"]`, fmt.Sprintf("%q", Lines("a\nb\nc")))
expect(t, `["a" "b" "c"]`, fmt.Sprintf("%q", Lines("a\nb\nc\n")))
expect(t, `["a" "b" "c" ""]`, fmt.Sprintf("%q", Lines("a\nb\nc\n\n")))
expect(t, `["" "a" "b" "c"]`, fmt.Sprintf("%q", Lines("\na\nb\nc\n")))
expect(t, `[]`, fmt.Sprintf("%q", Lines("")))
expect(t, `[]`, fmt.Sprintf("%q", Lines("\n")))
}

0 comments on commit 3474b32

Please sign in to comment.