1/*
2 * Licensed to the Apache Software Foundation (ASF) under one
3 * or more contributor license agreements. See the NOTICE file
4 * distributed with this work for additional information
5 * regarding copyright ownership. The ASF licenses this file
6 * to you under the Apache License, Version 2.0 (the
7 * "License"); you may not use this file except in compliance
8 * with the License. You may obtain a copy of the License at
9 *
10 *   http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing,
13 * software distributed under the License is distributed on an
14 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15 * KIND, either express or implied. See the License for the
16 * specific language governing permissions and limitations
17 * under the License.
18 */
19
20package thrift
21
22import (
23	"encoding/hex"
24	"fmt"
25)
26
27// Tuuid is a minimal implementation of UUID for thrift's read/write operations.
28//
29// This implementation only covers read/write in various thrift protocols.
30// If you need to generate/manipulate/etc. an UUID,
31// you likely would need a third party UUID library instead.
32//
33// This type should be directly cast-able with most popular third party UUID
34// libraries.
35// For example, assuming you are using
36// https://pkg.go.dev/github.com/google/uuid to generate a v4 UUID for an
37// optional thrift field:
38//
39//	id, err := uuid.NewRandom()
40//	if err != nil {
41//	  // TODO: handle errors
42//	}
43//	myRequest.Uuid = thrift.Pointer(thrift.Tuuid(id))
44type Tuuid [16]byte
45
46// String generates the canonical form string for an Tuuid.
47//
48// This string is suitable for writing with TJSONProtocol.
49func (u Tuuid) String() string {
50	var buf [36]byte
51	hex.Encode(buf[0:], u[:4])
52	buf[8] = '-'
53	hex.Encode(buf[9:], u[4:6])
54	buf[13] = '-'
55	hex.Encode(buf[14:], u[6:8])
56	buf[18] = '-'
57	hex.Encode(buf[19:], u[8:10])
58	buf[23] = '-'
59	hex.Encode(buf[24:], u[10:])
60	return string(buf[:])
61}
62
63func hexToDec(b byte) (byte, bool) {
64	switch {
65	case b >= '0' && b <= '9':
66		return b - '0', true
67	case b >= 'a' && b <= 'f':
68		return b - 'a' + 10, true
69	case b >= 'A' && b <= 'F':
70		return b - 'A' + 10, true
71	default:
72		return 0, false
73	}
74}
75
76func hexToByte(b1, b2 byte) (b byte, ok bool) {
77	b1, ok = hexToDec(b1)
78	if !ok {
79		return 0, ok
80	}
81	b2, ok = hexToDec(b2)
82	if !ok {
83		return 0, ok
84	}
85	return b1<<4 + b2, true
86}
87
88// ParseTuuid parses a canonical form UUID string into Tuuid.
89//
90// Note that this function only supports case insensitive canonical form
91// (8-4-4-4-12/xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx),
92// and rejects any other forms.
93// For a more flexible UUID string parser,
94// please use third party UUID libraries.
95//
96// This function is suitable for reading with TJSONProtocol.
97func ParseTuuid(s string) (u Tuuid, err error) {
98	if len(s) != 36 || s[8] != '-' || s[13] != '-' || s[18] != '-' || s[23] != '-' {
99		return u, fmt.Errorf("malformed Tuuid string: %q", s)
100	}
101	var ok bool
102	for i, j := range []int{
103		0, 2, 4, 6,
104		9, 11,
105		14, 16,
106		19, 21,
107		24, 26, 28, 30, 32, 34,
108	} {
109		u[i], ok = hexToByte(s[j], s[j+1])
110		if !ok {
111			return u, fmt.Errorf("malformed Tuuid string: %q", s)
112		}
113	}
114	return u, nil
115}
116
117// Must is a sugar to be used in places that error handling is impossible (for
118// example, global variable declarations) and also errors are not in general
119// expected.
120//
121// This is an example to use Must with ParseTuuid to declare a global special
122// uuid:
123//
124//	var NameSpaceDNSUUID = thrift.Must(thrift.ParseTuuid("6ba7b810-9dad-11d1-80b4-00c04fd430c8"))
125func Must[T any](v T, err error) T {
126	if err != nil {
127		panic(err)
128	}
129	return v
130}
131