276 lines
6.1 KiB
Go
276 lines
6.1 KiB
Go
|
package strutil
|
||
|
|
||
|
import (
|
||
|
"bytes"
|
||
|
"compress/flate"
|
||
|
"compress/gzip"
|
||
|
"encoding/base64"
|
||
|
"encoding/hex"
|
||
|
"encoding/json"
|
||
|
"errors"
|
||
|
"fmt"
|
||
|
"io"
|
||
|
"log"
|
||
|
"strings"
|
||
|
"tinyrdm/backend/types"
|
||
|
)
|
||
|
|
||
|
// ConvertTo convert string to specified type
|
||
|
// @param targetType empty string indicates automatic detection of the string type
|
||
|
func ConvertTo(str, targetType string) (value, resultType string) {
|
||
|
if len(str) <= 0 {
|
||
|
// empty content
|
||
|
if len(targetType) <= 0 {
|
||
|
resultType = types.PLAIN_TEXT
|
||
|
} else {
|
||
|
resultType = targetType
|
||
|
}
|
||
|
return
|
||
|
}
|
||
|
|
||
|
switch targetType {
|
||
|
case types.PLAIN_TEXT:
|
||
|
value = str
|
||
|
resultType = targetType
|
||
|
return
|
||
|
|
||
|
case types.JSON:
|
||
|
value, _ = decodeJson(str)
|
||
|
resultType = targetType
|
||
|
return
|
||
|
|
||
|
case types.BASE64_TEXT, types.BASE64_JSON:
|
||
|
if base64Str, ok := decodeBase64(str); ok {
|
||
|
if targetType == types.BASE64_JSON {
|
||
|
value, _ = decodeJson(base64Str)
|
||
|
} else {
|
||
|
value = base64Str
|
||
|
}
|
||
|
} else {
|
||
|
value = str
|
||
|
}
|
||
|
resultType = targetType
|
||
|
return
|
||
|
|
||
|
case types.HEX:
|
||
|
if hexStr, ok := decodeHex(str); ok {
|
||
|
log.Print(hexStr)
|
||
|
value = hexStr
|
||
|
} else {
|
||
|
value = str
|
||
|
}
|
||
|
resultType = targetType
|
||
|
return
|
||
|
|
||
|
case types.BINARY:
|
||
|
var binary strings.Builder
|
||
|
for _, char := range str {
|
||
|
binary.WriteString(fmt.Sprintf("%08b", int(char)))
|
||
|
}
|
||
|
value = binary.String()
|
||
|
resultType = targetType
|
||
|
return
|
||
|
|
||
|
case types.GZIP, types.GZIP_JSON:
|
||
|
if gzipStr, ok := decodeGZip(str); ok {
|
||
|
if targetType == types.BASE64_JSON {
|
||
|
value, _ = decodeJson(gzipStr)
|
||
|
} else {
|
||
|
value = gzipStr
|
||
|
}
|
||
|
} else {
|
||
|
value = str
|
||
|
}
|
||
|
resultType = targetType
|
||
|
return
|
||
|
|
||
|
case types.DEFLATE:
|
||
|
value, _ = decodeDeflate(str)
|
||
|
resultType = targetType
|
||
|
return
|
||
|
}
|
||
|
|
||
|
// type isn't specified or unknown, try to automatically detect and return converted value
|
||
|
return autoToType(str)
|
||
|
}
|
||
|
|
||
|
// attempt automatic convert to possible types
|
||
|
// if no conversion is possible, it will return the origin string value and "plain text" type
|
||
|
func autoToType(str string) (value, resultType string) {
|
||
|
if len(str) > 0 {
|
||
|
var ok bool
|
||
|
if value, ok = decodeJson(str); ok {
|
||
|
resultType = types.JSON
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if value, ok = decodeBase64(str); ok {
|
||
|
if value, ok = decodeJson(value); ok {
|
||
|
resultType = types.BASE64_JSON
|
||
|
return
|
||
|
}
|
||
|
resultType = types.BASE64_TEXT
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if value, ok = decodeGZip(str); ok {
|
||
|
resultType = types.GZIP
|
||
|
return
|
||
|
}
|
||
|
|
||
|
if value, ok = decodeDeflate(str); ok {
|
||
|
resultType = types.DEFLATE
|
||
|
return
|
||
|
}
|
||
|
}
|
||
|
|
||
|
value = str
|
||
|
resultType = types.PLAIN_TEXT
|
||
|
return
|
||
|
}
|
||
|
|
||
|
func decodeJson(str string) (string, bool) {
|
||
|
var data any
|
||
|
if (strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")) ||
|
||
|
(strings.HasPrefix(str, "[") && strings.HasSuffix(str, "]")) {
|
||
|
if err := json.Unmarshal([]byte(str), &data); err == nil {
|
||
|
var jsonByte []byte
|
||
|
if jsonByte, err = json.MarshalIndent(data, "", " "); err == nil {
|
||
|
return string(jsonByte), true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return str, false
|
||
|
}
|
||
|
|
||
|
func decodeBase64(str string) (string, bool) {
|
||
|
if decodedStr, err := base64.StdEncoding.DecodeString(str); err == nil {
|
||
|
return string(decodedStr), true
|
||
|
}
|
||
|
return str, false
|
||
|
}
|
||
|
|
||
|
func decodeHex(str string) (string, bool) {
|
||
|
encodeStr := hex.EncodeToString([]byte(str))
|
||
|
var resultStr strings.Builder
|
||
|
for i := 0; i < len(encodeStr); i += 2 {
|
||
|
resultStr.WriteString("\\x")
|
||
|
resultStr.WriteString(encodeStr[i : i+2])
|
||
|
}
|
||
|
return resultStr.String(), true
|
||
|
}
|
||
|
|
||
|
func decodeGZip(str string) (string, bool) {
|
||
|
if reader, err := gzip.NewReader(strings.NewReader(str)); err == nil {
|
||
|
defer reader.Close()
|
||
|
var decompressed []byte
|
||
|
if decompressed, err = io.ReadAll(reader); err == nil {
|
||
|
return string(decompressed), true
|
||
|
}
|
||
|
}
|
||
|
return str, false
|
||
|
}
|
||
|
|
||
|
func decodeDeflate(str string) (string, bool) {
|
||
|
reader := flate.NewReader(strings.NewReader(str))
|
||
|
defer reader.Close()
|
||
|
if decompressed, err := io.ReadAll(reader); err == nil {
|
||
|
return string(decompressed), true
|
||
|
}
|
||
|
return str, false
|
||
|
}
|
||
|
|
||
|
func SaveAs(str, targetType string) (value string, err error) {
|
||
|
switch targetType {
|
||
|
case types.PLAIN_TEXT:
|
||
|
return str, nil
|
||
|
|
||
|
case types.BASE64_TEXT:
|
||
|
base64Str, _ := encodeBase64(str)
|
||
|
return base64Str, nil
|
||
|
|
||
|
case types.JSON, types.BASE64_JSON, types.GZIP_JSON:
|
||
|
if jsonStr, ok := encodeJson(str); ok {
|
||
|
if targetType == types.BASE64_JSON {
|
||
|
base64Str, _ := encodeBase64(jsonStr)
|
||
|
return base64Str, nil
|
||
|
} else {
|
||
|
return jsonStr, nil
|
||
|
}
|
||
|
} else {
|
||
|
return str, errors.New("invalid json")
|
||
|
}
|
||
|
|
||
|
case types.GZIP:
|
||
|
if gzipStr, ok := encodeGZip(str); ok {
|
||
|
return gzipStr, nil
|
||
|
} else {
|
||
|
return str, errors.New("fail to build gzip data")
|
||
|
}
|
||
|
|
||
|
case types.DEFLATE:
|
||
|
if deflateStr, ok := encodeDeflate(str); ok {
|
||
|
return deflateStr, nil
|
||
|
} else {
|
||
|
return str, errors.New("fail to build deflate data")
|
||
|
}
|
||
|
}
|
||
|
return str, errors.New("fail to save with unknown error")
|
||
|
}
|
||
|
|
||
|
func encodeJson(str string) (string, bool) {
|
||
|
var data any
|
||
|
if (strings.HasPrefix(str, "{") && strings.HasSuffix(str, "}")) ||
|
||
|
(strings.HasPrefix(str, "[") && strings.HasSuffix(str, "]")) {
|
||
|
if err := json.Unmarshal([]byte(str), &data); err == nil {
|
||
|
var jsonByte []byte
|
||
|
if jsonByte, err = json.Marshal(data); err == nil {
|
||
|
return string(jsonByte), true
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
return str, false
|
||
|
}
|
||
|
|
||
|
func encodeBase64(str string) (string, bool) {
|
||
|
return base64.StdEncoding.EncodeToString([]byte(str)), true
|
||
|
}
|
||
|
|
||
|
func encodeGZip(str string) (string, bool) {
|
||
|
var compress = func(b []byte) (string, error) {
|
||
|
var buf bytes.Buffer
|
||
|
writer := gzip.NewWriter(&buf)
|
||
|
if _, err := writer.Write([]byte(str)); err != nil {
|
||
|
writer.Close()
|
||
|
return "", err
|
||
|
}
|
||
|
writer.Close()
|
||
|
return string(buf.Bytes()), nil
|
||
|
}
|
||
|
|
||
|
if gzipStr, err := compress([]byte(str)); err == nil {
|
||
|
return gzipStr, true
|
||
|
}
|
||
|
return str, false
|
||
|
}
|
||
|
|
||
|
func encodeDeflate(str string) (string, bool) {
|
||
|
var compress = func(b []byte) (string, error) {
|
||
|
var buf bytes.Buffer
|
||
|
writer, err := flate.NewWriter(&buf, flate.DefaultCompression)
|
||
|
if err != nil {
|
||
|
return "", err
|
||
|
}
|
||
|
if _, err = writer.Write([]byte(str)); err != nil {
|
||
|
writer.Close()
|
||
|
return "", err
|
||
|
}
|
||
|
writer.Close()
|
||
|
return string(buf.Bytes()), nil
|
||
|
}
|
||
|
if deflateStr, err := compress([]byte(str)); err == nil {
|
||
|
return deflateStr, true
|
||
|
}
|
||
|
return str, false
|
||
|
}
|