1// Copyright The OpenTelemetry Authors
2//
3// Licensed under the Apache License, Version 2.0 (the "License");
4// you may not use this file except in compliance with the License.
5// You may obtain a copy of the License at
6//
7//     http://www.apache.org/licenses/LICENSE-2.0
8//
9// Unless required by applicable law or agreed to in writing, software
10// distributed under the License is distributed on an "AS IS" BASIS,
11// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12// See the License for the specific language governing permissions and
13// limitations under the License.
14
15package attribute // import "go.opentelemetry.io/otel/attribute"
16
17import (
18	"encoding/json"
19	"fmt"
20	"reflect"
21	"strconv"
22
23	"go.opentelemetry.io/otel/internal"
24)
25
26//go:generate stringer -type=Type
27
28// Type describes the type of the data Value holds.
29type Type int
30
31// Value represents the value part in key-value pairs.
32type Value struct {
33	vtype    Type
34	numeric  uint64
35	stringly string
36	// TODO Lazy value type?
37
38	array interface{}
39}
40
41const (
42	// INVALID is used for a Value with no value set.
43	INVALID Type = iota
44	// BOOL is a boolean Type Value.
45	BOOL
46	// INT64 is a 64-bit signed integral Type Value.
47	INT64
48	// FLOAT64 is a 64-bit floating point Type Value.
49	FLOAT64
50	// STRING is a string Type Value.
51	STRING
52	// ARRAY is an array Type Value used to store 1-dimensional slices or
53	// arrays of bool, int, int32, int64, uint, uint32, uint64, float,
54	// float32, float64, or string types.
55	ARRAY
56)
57
58// BoolValue creates a BOOL Value.
59func BoolValue(v bool) Value {
60	return Value{
61		vtype:   BOOL,
62		numeric: internal.BoolToRaw(v),
63	}
64}
65
66// Int64Value creates an INT64 Value.
67func Int64Value(v int64) Value {
68	return Value{
69		vtype:   INT64,
70		numeric: internal.Int64ToRaw(v),
71	}
72}
73
74// Float64Value creates a FLOAT64 Value.
75func Float64Value(v float64) Value {
76	return Value{
77		vtype:   FLOAT64,
78		numeric: internal.Float64ToRaw(v),
79	}
80}
81
82// StringValue creates a STRING Value.
83func StringValue(v string) Value {
84	return Value{
85		vtype:    STRING,
86		stringly: v,
87	}
88}
89
90// IntValue creates an INT64 Value.
91func IntValue(v int) Value {
92	return Int64Value(int64(v))
93}
94
95// ArrayValue creates an ARRAY value from an array or slice.
96// Only arrays or slices of bool, int, int64, float, float64, or string types are allowed.
97// Specifically, arrays  and slices can not contain other arrays, slices, structs, or non-standard
98// types. If the passed value is not an array or slice of these types an
99// INVALID value is returned.
100func ArrayValue(v interface{}) Value {
101	switch reflect.TypeOf(v).Kind() {
102	case reflect.Array, reflect.Slice:
103		// get array type regardless of dimensions
104		typ := reflect.TypeOf(v).Elem()
105		kind := typ.Kind()
106		switch kind {
107		case reflect.Bool, reflect.Int, reflect.Int64,
108			reflect.Float64, reflect.String:
109			val := reflect.ValueOf(v)
110			length := val.Len()
111			frozen := reflect.Indirect(reflect.New(reflect.ArrayOf(length, typ)))
112			reflect.Copy(frozen, val)
113			return Value{
114				vtype: ARRAY,
115				array: frozen.Interface(),
116			}
117		default:
118			return Value{vtype: INVALID}
119		}
120	}
121	return Value{vtype: INVALID}
122}
123
124// Type returns a type of the Value.
125func (v Value) Type() Type {
126	return v.vtype
127}
128
129// AsBool returns the bool value. Make sure that the Value's type is
130// BOOL.
131func (v Value) AsBool() bool {
132	return internal.RawToBool(v.numeric)
133}
134
135// AsInt64 returns the int64 value. Make sure that the Value's type is
136// INT64.
137func (v Value) AsInt64() int64 {
138	return internal.RawToInt64(v.numeric)
139}
140
141// AsFloat64 returns the float64 value. Make sure that the Value's
142// type is FLOAT64.
143func (v Value) AsFloat64() float64 {
144	return internal.RawToFloat64(v.numeric)
145}
146
147// AsString returns the string value. Make sure that the Value's type
148// is STRING.
149func (v Value) AsString() string {
150	return v.stringly
151}
152
153// AsArray returns the array Value as an interface{}.
154func (v Value) AsArray() interface{} {
155	return v.array
156}
157
158type unknownValueType struct{}
159
160// AsInterface returns Value's data as interface{}.
161func (v Value) AsInterface() interface{} {
162	switch v.Type() {
163	case ARRAY:
164		return v.AsArray()
165	case BOOL:
166		return v.AsBool()
167	case INT64:
168		return v.AsInt64()
169	case FLOAT64:
170		return v.AsFloat64()
171	case STRING:
172		return v.stringly
173	}
174	return unknownValueType{}
175}
176
177// Emit returns a string representation of Value's data.
178func (v Value) Emit() string {
179	switch v.Type() {
180	case ARRAY:
181		return fmt.Sprint(v.array)
182	case BOOL:
183		return strconv.FormatBool(v.AsBool())
184	case INT64:
185		return strconv.FormatInt(v.AsInt64(), 10)
186	case FLOAT64:
187		return fmt.Sprint(v.AsFloat64())
188	case STRING:
189		return v.stringly
190	default:
191		return "unknown"
192	}
193}
194
195// MarshalJSON returns the JSON encoding of the Value.
196func (v Value) MarshalJSON() ([]byte, error) {
197	var jsonVal struct {
198		Type  string
199		Value interface{}
200	}
201	jsonVal.Type = v.Type().String()
202	jsonVal.Value = v.AsInterface()
203	return json.Marshal(jsonVal)
204}
205