2024-01-27 14:36:14 +08:00
|
|
|
package convutil
|
|
|
|
|
|
|
|
import (
|
|
|
|
"errors"
|
|
|
|
"regexp"
|
|
|
|
"tinyrdm/backend/types"
|
|
|
|
strutil "tinyrdm/backend/utils/string"
|
|
|
|
)
|
|
|
|
|
|
|
|
type DataConvert interface {
|
2024-02-20 10:55:46 +08:00
|
|
|
Enable() bool
|
2024-01-27 14:36:14 +08:00
|
|
|
Encode(string) (string, bool)
|
|
|
|
Decode(string) (string, bool)
|
|
|
|
}
|
|
|
|
|
|
|
|
var (
|
|
|
|
jsonConv JsonConvert
|
2024-02-21 15:07:39 +08:00
|
|
|
uniJsonConv UnicodeJsonConvert
|
2024-01-30 01:30:44 +08:00
|
|
|
yamlConv YamlConvert
|
|
|
|
xmlConv XmlConvert
|
2024-01-27 14:36:14 +08:00
|
|
|
base64Conv Base64Convert
|
|
|
|
binaryConv BinaryConvert
|
|
|
|
hexConv HexConvert
|
|
|
|
gzipConv GZipConvert
|
|
|
|
deflateConv DeflateConvert
|
|
|
|
zstdConv ZStdConvert
|
2024-07-31 16:48:03 +08:00
|
|
|
lz4Conv LZ4Convert
|
2024-01-27 14:36:14 +08:00
|
|
|
brotliConv BrotliConvert
|
|
|
|
msgpackConv MsgpackConvert
|
2024-02-20 10:55:46 +08:00
|
|
|
phpConv = NewPhpConvert()
|
|
|
|
pickleConv = NewPickleConvert()
|
2024-01-27 14:36:14 +08:00
|
|
|
)
|
|
|
|
|
2024-02-20 10:55:46 +08:00
|
|
|
var BuildInFormatters = map[string]DataConvert{
|
2024-02-21 15:07:39 +08:00
|
|
|
types.FORMAT_JSON: jsonConv,
|
|
|
|
types.FORMAT_UNICODE_JSON: uniJsonConv,
|
|
|
|
types.FORMAT_YAML: yamlConv,
|
|
|
|
types.FORMAT_XML: xmlConv,
|
|
|
|
types.FORMAT_HEX: hexConv,
|
|
|
|
types.FORMAT_BINARY: binaryConv,
|
2024-02-20 10:55:46 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
var BuildInDecoders = map[string]DataConvert{
|
|
|
|
types.DECODE_BASE64: base64Conv,
|
|
|
|
types.DECODE_GZIP: gzipConv,
|
|
|
|
types.DECODE_DEFLATE: deflateConv,
|
|
|
|
types.DECODE_ZSTD: zstdConv,
|
2024-07-31 16:48:03 +08:00
|
|
|
types.DECODE_LZ4: lz4Conv,
|
2024-02-20 10:55:46 +08:00
|
|
|
types.DECODE_BROTLI: brotliConv,
|
|
|
|
types.DECODE_MSGPACK: msgpackConv,
|
|
|
|
types.DECODE_PHP: phpConv,
|
|
|
|
types.DECODE_PICKLE: pickleConv,
|
|
|
|
}
|
|
|
|
|
2024-01-27 14:36:14 +08:00
|
|
|
// ConvertTo convert string to specified type
|
|
|
|
// @param decodeType empty string indicates automatic detection
|
|
|
|
// @param formatType empty string indicates automatic detection
|
2024-02-03 15:06:23 +08:00
|
|
|
// @param custom decoder if any
|
|
|
|
func ConvertTo(str, decodeType, formatType string, customDecoder []CmdConvert) (value, resultDecode, resultFormat string) {
|
2024-01-27 14:36:14 +08:00
|
|
|
if len(str) <= 0 {
|
|
|
|
// empty content
|
|
|
|
if len(formatType) <= 0 {
|
|
|
|
resultFormat = types.FORMAT_RAW
|
|
|
|
} else {
|
|
|
|
resultFormat = formatType
|
|
|
|
}
|
|
|
|
if len(decodeType) <= 0 {
|
|
|
|
resultDecode = types.DECODE_NONE
|
|
|
|
} else {
|
|
|
|
resultDecode = decodeType
|
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// decode first
|
2024-02-03 15:06:23 +08:00
|
|
|
value, resultDecode = decodeWith(str, decodeType, customDecoder)
|
2024-01-27 14:36:14 +08:00
|
|
|
// then format content
|
2024-02-03 15:06:23 +08:00
|
|
|
if len(formatType) <= 0 {
|
|
|
|
value, resultFormat = autoViewAs(value)
|
|
|
|
} else {
|
|
|
|
value, resultFormat = viewAs(value, formatType)
|
|
|
|
}
|
2024-01-27 14:36:14 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-02-03 15:06:23 +08:00
|
|
|
func decodeWith(str, decodeType string, customDecoder []CmdConvert) (value, resultDecode string) {
|
2024-01-27 14:36:14 +08:00
|
|
|
if len(decodeType) > 0 {
|
2024-02-20 10:55:46 +08:00
|
|
|
value = str
|
2024-01-27 14:36:14 +08:00
|
|
|
|
2024-02-20 10:55:46 +08:00
|
|
|
if buildinDecoder, ok := BuildInDecoders[decodeType]; ok {
|
|
|
|
if decodedStr, ok := buildinDecoder.Decode(str); ok {
|
|
|
|
value = decodedStr
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
2024-02-20 10:55:46 +08:00
|
|
|
} else if decodeType != types.DECODE_NONE {
|
2024-02-03 15:06:23 +08:00
|
|
|
for _, decoder := range customDecoder {
|
|
|
|
if decoder.Name == decodeType {
|
|
|
|
if decodedStr, ok := decoder.Decode(str); ok {
|
|
|
|
value = decodedStr
|
|
|
|
}
|
|
|
|
break
|
|
|
|
}
|
|
|
|
}
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
2024-02-03 15:06:23 +08:00
|
|
|
|
2024-01-27 14:36:14 +08:00
|
|
|
resultDecode = decodeType
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-02-03 15:06:23 +08:00
|
|
|
value, resultDecode = autoDecode(str, customDecoder)
|
|
|
|
return
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// attempt try possible decode method
|
|
|
|
// if no decode is possible, it will return the origin string value and "none" decode type
|
2024-02-03 15:06:23 +08:00
|
|
|
func autoDecode(str string, customDecoder []CmdConvert) (value, resultDecode string) {
|
2024-01-27 14:36:14 +08:00
|
|
|
if len(str) > 0 {
|
|
|
|
// pure digit content may incorrect regard as some encoded type, skip decode
|
|
|
|
if match, _ := regexp.MatchString(`^\d+$`, str); !match {
|
|
|
|
var ok bool
|
|
|
|
if len(str)%4 == 0 && len(str) >= 12 && !strutil.IsSameChar(str) {
|
|
|
|
if value, ok = base64Conv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_BASE64
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if value, ok = gzipConv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_GZIP
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
// FIXME: skip decompress with deflate due to incorrect format checking
|
|
|
|
//if value, ok = decodeDeflate(str); ok {
|
|
|
|
// resultDecode = types.DECODE_DEFLATE
|
|
|
|
// return
|
|
|
|
//}
|
|
|
|
|
|
|
|
if value, ok = zstdConv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_ZSTD
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-07-31 16:48:03 +08:00
|
|
|
if value, ok = lz4Conv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_LZ4
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-27 14:36:14 +08:00
|
|
|
// FIXME: skip decompress with brotli due to incorrect format checking
|
|
|
|
//if value, ok = decodeBrotli(str); ok {
|
|
|
|
// resultDecode = types.DECODE_BROTLI
|
|
|
|
// return
|
|
|
|
//}
|
|
|
|
|
|
|
|
if value, ok = msgpackConv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_MSGPACK
|
|
|
|
return
|
|
|
|
}
|
2024-02-03 15:06:23 +08:00
|
|
|
|
2024-02-20 10:55:46 +08:00
|
|
|
if value, ok = phpConv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_PHP
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if value, ok = pickleConv.Decode(str); ok {
|
|
|
|
resultDecode = types.DECODE_PICKLE
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-02-03 15:06:23 +08:00
|
|
|
// try decode with custom decoder
|
|
|
|
for _, decoder := range customDecoder {
|
|
|
|
if decoder.Auto {
|
|
|
|
if value, ok = decoder.Decode(str); ok {
|
|
|
|
resultDecode = decoder.Name
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
value = str
|
|
|
|
resultDecode = types.DECODE_NONE
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
func viewAs(str, formatType string) (value, resultFormat string) {
|
|
|
|
if len(formatType) > 0 {
|
2024-02-20 10:55:46 +08:00
|
|
|
value = str
|
|
|
|
if buildinFormatter, ok := BuildInFormatters[formatType]; ok {
|
|
|
|
if formattedStr, ok := buildinFormatter.Decode(str); ok {
|
|
|
|
value = formattedStr
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
|
|
|
}
|
|
|
|
resultFormat = formatType
|
|
|
|
return
|
|
|
|
}
|
2024-02-03 15:06:23 +08:00
|
|
|
return
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
|
|
|
|
|
|
|
// attempt automatic convert to possible types
|
|
|
|
// if no conversion is possible, it will return the origin string value and "plain text" type
|
|
|
|
func autoViewAs(str string) (value, resultFormat string) {
|
|
|
|
if len(str) > 0 {
|
|
|
|
var ok bool
|
|
|
|
if value, ok = jsonConv.Decode(str); ok {
|
|
|
|
resultFormat = types.FORMAT_JSON
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-30 01:30:44 +08:00
|
|
|
if value, ok = yamlConv.Decode(str); ok {
|
|
|
|
resultFormat = types.FORMAT_YAML
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
|
|
|
if value, ok = xmlConv.Decode(str); ok {
|
|
|
|
resultFormat = types.FORMAT_XML
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-01-27 14:36:14 +08:00
|
|
|
if strutil.ContainsBinary(str) {
|
|
|
|
if value, ok = hexConv.Decode(str); ok {
|
|
|
|
resultFormat = types.FORMAT_HEX
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
value = str
|
|
|
|
resultFormat = types.FORMAT_RAW
|
|
|
|
return
|
|
|
|
}
|
|
|
|
|
2024-02-03 15:06:23 +08:00
|
|
|
func SaveAs(str, format, decode string, customDecoder []CmdConvert) (value string, err error) {
|
2024-01-27 14:36:14 +08:00
|
|
|
value = str
|
2024-02-20 10:55:46 +08:00
|
|
|
if buildingFormatter, ok := BuildInFormatters[format]; ok {
|
|
|
|
if formattedStr, ok := buildingFormatter.Encode(str); ok {
|
|
|
|
value = formattedStr
|
2024-01-27 14:36:14 +08:00
|
|
|
} else {
|
2024-02-20 10:55:46 +08:00
|
|
|
err = errors.New("invalid " + format + " data")
|
2024-01-27 14:36:14 +08:00
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2024-02-20 10:55:46 +08:00
|
|
|
if buildinDecoder, ok := BuildInDecoders[decode]; ok {
|
|
|
|
if encodedValue, ok := buildinDecoder.Encode(str); ok {
|
|
|
|
value = encodedValue
|
2024-01-27 14:36:14 +08:00
|
|
|
} else {
|
2024-02-20 10:55:46 +08:00
|
|
|
err = errors.New("fail to build " + decode)
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
|
|
|
return
|
2024-02-20 10:55:46 +08:00
|
|
|
} else if decode != types.DECODE_NONE {
|
2024-02-03 15:06:23 +08:00
|
|
|
for _, decoder := range customDecoder {
|
|
|
|
if decoder.Name == decode {
|
|
|
|
if encodedStr, ok := decoder.Encode(str); ok {
|
|
|
|
value = encodedStr
|
|
|
|
} else {
|
2024-02-20 10:55:46 +08:00
|
|
|
err = errors.New("fail to build " + decode)
|
2024-02-03 15:06:23 +08:00
|
|
|
}
|
|
|
|
return
|
|
|
|
}
|
|
|
|
}
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|
2024-02-28 18:21:31 +08:00
|
|
|
return
|
2024-01-27 14:36:14 +08:00
|
|
|
}
|