mirror of
				https://github.com/tiny-craft/tiny-rdm.git
				synced 2025-10-31 10:36:22 +08:00 
			
		
		
		
	
		
			
				
	
	
		
			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
 | |
| }
 | 
