summaryrefslogtreecommitdiff
path: root/pkg/util
diff options
context:
space:
mode:
authorLuke Shumaker <lukeshu@lukeshu.com>2022-06-26 20:25:27 -0600
committerLuke Shumaker <lukeshu@lukeshu.com>2022-06-26 20:27:11 -0600
commit8db7bed06505569c1b41a338b5131cfd5c9d1f28 (patch)
treea33404699ab627cc71c40bdbe7062ef837c9d7d3 /pkg/util
parent09e3317a433add776d6539abcd7cf4f00ca984e5 (diff)
tidy up which package things are in
Diffstat (limited to 'pkg/util')
-rw-r--r--pkg/util/uuid.go72
-rw-r--r--pkg/util/uuid_test.go63
2 files changed, 135 insertions, 0 deletions
diff --git a/pkg/util/uuid.go b/pkg/util/uuid.go
new file mode 100644
index 0000000..3b4cbaf
--- /dev/null
+++ b/pkg/util/uuid.go
@@ -0,0 +1,72 @@
+package util
+
+import (
+ "encoding/hex"
+ "fmt"
+ "strings"
+)
+
+type UUID [16]byte
+
+func (uuid UUID) String() string {
+ str := hex.EncodeToString(uuid[:])
+ return strings.Join([]string{
+ str[:8],
+ str[8:12],
+ str[12:16],
+ str[16:20],
+ str[20:32],
+ }, "-")
+}
+
+func (a UUID) Cmp(b UUID) int {
+ for i := range a {
+ if d := int(a[i]) - int(b[i]); d != 0 {
+ return d
+ }
+ }
+ return 0
+}
+
+func (uuid UUID) Format(f fmt.State, verb rune) {
+ FormatByteArrayStringer(uuid, uuid[:], f, verb)
+}
+
+func ParseUUID(str string) (UUID, error) {
+ var ret UUID
+ j := 0
+ for i := 0; i < len(str); i++ {
+ if j >= len(ret)*2 {
+ return UUID{}, fmt.Errorf("too long to be a UUID: %q|%q", str[:i], str[i:])
+ }
+ c := str[i]
+ var v byte
+ switch {
+ case '0' <= c && c <= '9':
+ v = c - '0'
+ case 'a' <= c && c <= 'f':
+ v = c - 'a' + 10
+ case 'A' <= c && c <= 'F':
+ v = c - 'A' + 10
+ case c == '-':
+ continue
+ default:
+ return UUID{}, fmt.Errorf("illegal byte in UUID: %q|%q|%q", str[:i], str[i:i+1], str[i+1:])
+ }
+ if j%2 == 0 {
+ ret[j/2] = v << 4
+ } else {
+ ret[j/2] = (ret[j/2] & 0xf0) | (v & 0x0f)
+ }
+ j++
+ }
+ return ret, nil
+}
+
+func MustParseUUID(str string) UUID {
+ ret, err := ParseUUID(str)
+ if err != nil {
+ panic(err)
+ }
+ return ret
+}
diff --git a/pkg/util/uuid_test.go b/pkg/util/uuid_test.go
new file mode 100644
index 0000000..8b3302a
--- /dev/null
+++ b/pkg/util/uuid_test.go
@@ -0,0 +1,63 @@
+package util_test
+
+import (
+ "fmt"
+ "testing"
+
+ "github.com/stretchr/testify/assert"
+
+ "lukeshu.com/btrfs-tools/pkg/util"
+)
+
+func TestParseUUID(t *testing.T) {
+ t.Parallel()
+ type TestCase struct {
+ Input string
+ OutputVal util.UUID
+ OutputErr string
+ }
+ testcases := map[string]TestCase{
+ "basic": TestCase{Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43", OutputVal: util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0x0c, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}},
+ "too-long": TestCase{Input: "a0dd94ed-e60c-42e8-8632-64e8d4765a43a", OutputErr: `too long to be a UUID: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"|"a"`},
+ "bad char": TestCase{Input: "a0dd94ej-e60c-42e8-8632-64e8d4765a43a", OutputErr: `illegal byte in UUID: "a0dd94e"|"j"|"-e60c-42e8-8632-64e8d4765a43a"`},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ val, err := util.ParseUUID(tc.Input)
+ assert.Equal(t, tc.OutputVal, val)
+ if tc.OutputErr == "" {
+ assert.NoError(t, err)
+ } else {
+ assert.EqualError(t, err, tc.OutputErr)
+ }
+ })
+ }
+}
+
+func TestUUIDFormat(t *testing.T) {
+ t.Parallel()
+ type TestCase struct {
+ InputUUID util.UUID
+ InputFmt string
+ Output string
+ }
+ uuid := util.MustParseUUID("a0dd94ed-e60c-42e8-8632-64e8d4765a43")
+ testcases := map[string]TestCase{
+ "s": TestCase{InputUUID: uuid, InputFmt: "%s", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"},
+ "x": TestCase{InputUUID: uuid, InputFmt: "%x", Output: "a0dd94ede60c42e8863264e8d4765a43"},
+ "X": TestCase{InputUUID: uuid, InputFmt: "%X", Output: "A0DD94EDE60C42E8863264E8D4765A43"},
+ "v": TestCase{InputUUID: uuid, InputFmt: "%v", Output: "a0dd94ed-e60c-42e8-8632-64e8d4765a43"},
+ "40s": TestCase{InputUUID: uuid, InputFmt: "|% 40s", Output: "| a0dd94ed-e60c-42e8-8632-64e8d4765a43"},
+ "#115v": TestCase{InputUUID: uuid, InputFmt: "|%#115v", Output: "| util.UUID{0xa0, 0xdd, 0x94, 0xed, 0xe6, 0xc, 0x42, 0xe8, 0x86, 0x32, 0x64, 0xe8, 0xd4, 0x76, 0x5a, 0x43}"},
+ }
+ for tcName, tc := range testcases {
+ tc := tc
+ t.Run(tcName, func(t *testing.T) {
+ t.Parallel()
+ actual := fmt.Sprintf(tc.InputFmt, tc.InputUUID)
+ assert.Equal(t, tc.Output, actual)
+ })
+ }
+}