diff --git a/util/strs/strs.go b/util/strs/strs.go new file mode 100644 index 000000000..40d4b12bf --- /dev/null +++ b/util/strs/strs.go @@ -0,0 +1,36 @@ +// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +// Package strs contains string-related utility funcs. +package strs + +import "strings" + +// CutPrefix returns s without the provided leading prefix string +// and reports whether it found the prefix. +// If s doesn't start with prefix, CutPrefix returns s, false. +// If prefix is the empty string, CutPrefix returns s, true. +// +// TODO: remove this once Go 1.20 is out with it. +// See https://github.com/tailscale/tailscale/issues/5309 +func CutPrefix(s, prefix string) (after string, found bool) { + if !strings.HasPrefix(s, prefix) { + return s, false + } + return s[len(prefix):], true +} + +// CutSuffix returns s without the provided ending suffix string +// and reports whether it found the suffix. +// If s doesn't end with suffix, CutSuffix returns s, false. +// If suffix is the empty string, CutSuffix returns s, true. +// +// See https://github.com/tailscale/tailscale/issues/5309 +// TODO: remove this once Go 1.20 is out with it. +func CutSuffix(s, suffix string) (before string, found bool) { + if !strings.HasSuffix(s, suffix) { + return s, false + } + return s[:len(s)-len(suffix)], true +} diff --git a/util/strs/strs_test.go b/util/strs/strs_test.go new file mode 100644 index 000000000..3ac416d2c --- /dev/null +++ b/util/strs/strs_test.go @@ -0,0 +1,30 @@ +// Copyright (c) 2022 Tailscale Inc & AUTHORS All rights reserved. +// Use of this source code is governed by a BSD-style +// license that can be found in the LICENSE file. + +package strs + +import "testing" + +func TestCut(t *testing.T) { + tests := []struct { + fn func(string, string) (string, bool) + in1, in2 string + want string + wantOK bool + }{ + {CutPrefix, "foo", "fo", "o", true}, + {CutPrefix, "bar", "fo", "bar", false}, + {CutSuffix, "foo", "o", "fo", true}, + {CutSuffix, "bar", "fo", "bar", false}, + } + for i, tt := range tests { + got, gotOK := tt.fn(tt.in1, tt.in2) + if got != tt.want { + t.Errorf("%d. got %q; want %q", i, got, tt.want) + } + if gotOK != tt.wantOK { + t.Errorf("%d. got %v; want %v", i, gotOK, tt.wantOK) + } + } +}