1//
2// Copyright 2021, Sander van Harmelen
3//
4// Licensed under the Apache License, Version 2.0 (the "License");
5// you may not use this file except in compliance with the License.
6// You may obtain a copy of the License at
7//
8//     http://www.apache.org/licenses/LICENSE-2.0
9//
10// Unless required by applicable law or agreed to in writing, software
11// distributed under the License is distributed on an "AS IS" BASIS,
12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13// See the License for the specific language governing permissions and
14// limitations under the License.
15//
16
17package gitlab
18
19import (
20	"bytes"
21	"fmt"
22
23	"reflect"
24)
25
26// Stringify attempts to create a reasonable string representation of types in
27// the Gitlab library.  It does things like resolve pointers to their values
28// and omits struct fields with nil values.
29func Stringify(message interface{}) string {
30	var buf bytes.Buffer
31	v := reflect.ValueOf(message)
32	stringifyValue(&buf, v)
33	return buf.String()
34}
35
36// stringifyValue was heavily inspired by the goprotobuf library.
37func stringifyValue(buf *bytes.Buffer, val reflect.Value) {
38	if val.Kind() == reflect.Ptr && val.IsNil() {
39		buf.WriteString("<nil>")
40		return
41	}
42
43	v := reflect.Indirect(val)
44
45	switch v.Kind() {
46	case reflect.String:
47		fmt.Fprintf(buf, `"%s"`, v)
48	case reflect.Slice:
49		buf.WriteByte('[')
50		for i := 0; i < v.Len(); i++ {
51			if i > 0 {
52				buf.WriteByte(' ')
53			}
54
55			stringifyValue(buf, v.Index(i))
56		}
57
58		buf.WriteByte(']')
59		return
60	case reflect.Struct:
61		if v.Type().Name() != "" {
62			buf.WriteString(v.Type().String())
63		}
64
65		buf.WriteByte('{')
66
67		var sep bool
68		for i := 0; i < v.NumField(); i++ {
69			fv := v.Field(i)
70			if fv.Kind() == reflect.Ptr && fv.IsNil() {
71				continue
72			}
73			if fv.Kind() == reflect.Slice && fv.IsNil() {
74				continue
75			}
76
77			if sep {
78				buf.WriteString(", ")
79			} else {
80				sep = true
81			}
82
83			buf.WriteString(v.Type().Field(i).Name)
84			buf.WriteByte(':')
85			stringifyValue(buf, fv)
86		}
87
88		buf.WriteByte('}')
89	default:
90		if v.CanInterface() {
91			fmt.Fprint(buf, v.Interface())
92		}
93	}
94}
95