1// Copyright 2017 The Gitea Authors. All rights reserved.
2// Use of this source code is governed by a MIT-style
3// license that can be found in the LICENSE file.
4
5package util
6
7import (
8	"bytes"
9	"crypto/rand"
10	"errors"
11	"math/big"
12	"strconv"
13	"strings"
14)
15
16// OptionalBool a boolean that can be "null"
17type OptionalBool byte
18
19const (
20	// OptionalBoolNone a "null" boolean value
21	OptionalBoolNone OptionalBool = iota
22	// OptionalBoolTrue a "true" boolean value
23	OptionalBoolTrue
24	// OptionalBoolFalse a "false" boolean value
25	OptionalBoolFalse
26)
27
28// IsTrue return true if equal to OptionalBoolTrue
29func (o OptionalBool) IsTrue() bool {
30	return o == OptionalBoolTrue
31}
32
33// IsFalse return true if equal to OptionalBoolFalse
34func (o OptionalBool) IsFalse() bool {
35	return o == OptionalBoolFalse
36}
37
38// IsNone return true if equal to OptionalBoolNone
39func (o OptionalBool) IsNone() bool {
40	return o == OptionalBoolNone
41}
42
43// OptionalBoolOf get the corresponding OptionalBool of a bool
44func OptionalBoolOf(b bool) OptionalBool {
45	if b {
46		return OptionalBoolTrue
47	}
48	return OptionalBoolFalse
49}
50
51// OptionalBoolParse get the corresponding OptionalBool of a string using strconv.ParseBool
52func OptionalBoolParse(s string) OptionalBool {
53	b, e := strconv.ParseBool(s)
54	if e != nil {
55		return OptionalBoolNone
56	}
57	return OptionalBoolOf(b)
58}
59
60// Max max of two ints
61func Max(a, b int) int {
62	if a < b {
63		return b
64	}
65	return a
66}
67
68// Min min of two ints
69func Min(a, b int) int {
70	if a > b {
71		return b
72	}
73	return a
74}
75
76// IsEmptyString checks if the provided string is empty
77func IsEmptyString(s string) bool {
78	return len(strings.TrimSpace(s)) == 0
79}
80
81// NormalizeEOL will convert Windows (CRLF) and Mac (CR) EOLs to UNIX (LF)
82func NormalizeEOL(input []byte) []byte {
83	var right, left, pos int
84	if right = bytes.IndexByte(input, '\r'); right == -1 {
85		return input
86	}
87	length := len(input)
88	tmp := make([]byte, length)
89
90	// We know that left < length because otherwise right would be -1 from IndexByte.
91	copy(tmp[pos:pos+right], input[left:left+right])
92	pos += right
93	tmp[pos] = '\n'
94	left += right + 1
95	pos++
96
97	for left < length {
98		if input[left] == '\n' {
99			left++
100		}
101
102		right = bytes.IndexByte(input[left:], '\r')
103		if right == -1 {
104			copy(tmp[pos:], input[left:])
105			pos += length - left
106			break
107		}
108		copy(tmp[pos:pos+right], input[left:left+right])
109		pos += right
110		tmp[pos] = '\n'
111		left += right + 1
112		pos++
113	}
114	return tmp[:pos]
115}
116
117// MergeInto merges pairs of values into a "dict"
118func MergeInto(dict map[string]interface{}, values ...interface{}) (map[string]interface{}, error) {
119	for i := 0; i < len(values); i++ {
120		switch key := values[i].(type) {
121		case string:
122			i++
123			if i == len(values) {
124				return nil, errors.New("specify the key for non array values")
125			}
126			dict[key] = values[i]
127		case map[string]interface{}:
128			m := values[i].(map[string]interface{})
129			for i, v := range m {
130				dict[i] = v
131			}
132		default:
133			return nil, errors.New("dict values must be maps")
134		}
135	}
136
137	return dict, nil
138}
139
140// RandomInt returns a random integer between 0 and limit, inclusive
141func RandomInt(limit int64) (int64, error) {
142	rInt, err := rand.Int(rand.Reader, big.NewInt(limit))
143	if err != nil {
144		return 0, err
145	}
146	return rInt.Int64(), nil
147}
148
149const letters = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
150
151// RandomString generates a random alphanumerical string
152func RandomString(length int64) (string, error) {
153	bytes := make([]byte, length)
154	limit := int64(len(letters))
155	for i := range bytes {
156		num, err := RandomInt(limit)
157		if err != nil {
158			return "", err
159		}
160		bytes[i] = letters[num]
161	}
162	return string(bytes), nil
163}
164
165// RandomBytes generates `length` bytes
166// This differs from RandomString, as RandomString is limits each byte to have
167// a maximum value of 63 instead of 255(max byte size)
168func RandomBytes(length int64) ([]byte, error) {
169	bytes := make([]byte, length)
170	_, err := rand.Read(bytes)
171	return bytes, err
172}
173