diff --git a/string.go b/string.go index 8b24010..6b80dac 100644 --- a/string.go +++ b/string.go @@ -1,6 +1,7 @@ package gostrutils import ( + "errors" "strings" "unicode/utf8" ) @@ -53,3 +54,33 @@ func Truncate(s string, length int) string { return string(result) } + +// CopyRange returns a copy of a string based on start and end of a rune +// for multi-byte chars instead of byte based chars (ASCII). +// 'to' is the amount of chars interested plus one. +// for example, for a string 'foo' extracting 'oo' by from: 1 and to 3. +func CopyRange(src string, from, to int) (string, error) { + if src == "" { + return src, nil + } + tmp := []rune(src) + + runeLength := len(tmp) + if to <= 0 { + to = runeLength + } + + if from >= runeLength { + return "", errors.New("from is larger then length") + } + + if to > runeLength+1 { + return "", errors.New("to is bigger then length") + } + + if (from + to) > runeLength+2 { + return "", errors.New("from + to is out of range") + } + + return string(tmp[from:to]), nil +} diff --git a/string_test.go b/string_test.go index 19f425d..38f7435 100644 --- a/string_test.go +++ b/string_test.go @@ -143,3 +143,96 @@ func TestTruncate(t *testing.T) { } } } + +func TestCopyRange(t *testing.T) { + type toCheck struct { + input string + from int + to int + expected string + haveError bool + error string + } + tests := []toCheck{ + { + input: "foo", + from: 3, + to: 1, + haveError: true, + error: "from is larger then length", + }, + { + input: "foo", + from: 0, + to: 5, + haveError: true, + error: "to is bigger then length", + }, + { + input: "foo", + from: 2, + to: 4, + haveError: true, + error: "from + to is out of range", + }, + { + input: "foo", + from: 1, + to: 3, + expected: "oo", + }, + { + input: "עברית", + from: 0, + to: 3, + expected: "עבר", + }, + { + input: "עברית", + from: 1, + to: 4, + expected: "ברי", + }, + { + // start with 1 + input: "1עברית", + from: 1, + to: 6, + expected: "עברית", + }, + { + // end with 1 + input: "עברית1", + from: 0, + to: 5, + expected: "עברית", + }, + { + input: "‏1עברית", + from: 2, + to: 0, + expected: "עברית", + }, + } + + for idx, test := range tests { + result, err := CopyRange(test.input, test.from, test.to) + if err != nil { + if !test.haveError { + t.Errorf("Unexpected err: %s", err) + continue + } + if err.Error() != test.error { + t.Errorf("Expected error %d: '%s', got '%s'", idx, test.error, err) + } + continue + } + + if test.expected != result { + t.Errorf("test %d '%s' (%d): expected '%s'[%d:%d], got :'%s' %x", + idx, test.input, len(test.input), + test.expected, test.from, test.to, result, result) + } + } + +}