obrimbasecli/essential/visible/service/helper/codec/codec.go

499 lines
12 KiB
Go

/*
|--------------------------------------------------------------------------
| Codec
|--------------------------------------------------------------------------
|
| Name:
| - Codec
|
| Purpose:
| - Provide unified encoding and decoding capabilities using Base8,
| Base10, Base16, Base32, Base64, and Sqids with centralized
| validation, dispatching, transformation handling, and structured
| result processing.
|
| Guidelines:
| - Validate all required parameters before execution.
| - Reject unsupported operations and formats.
| - Return standardized structured responses.
| - Prevent application crashes through safe error handling.
|
| Examples:
| - ObrimCodec("encode", config)
| - ObrimCodec("decode", config)
|
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Credit
|--------------------------------------------------------------------------
|
| Contributor:
| - Rajon Ahmed
| - Scionite
|
|--------------------------------------------------------------------------
*/
package codec
import (
"encoding/base32"
"encoding/base64"
"encoding/hex"
"fmt"
"math/big"
"strconv"
"strings"
"github.com/sqids/sqids-go"
)
// Function Purpose: Standardized utility response structure.
type ObrimCodecResponse struct {
Status bool `json:"status"`
Code string `json:"code"`
Payload map[string]any `json:"payload"`
}
// Function Purpose: Validate configuration, dispatch codec operations, and return structured results.
func ObrimCodec(operation string, config map[string]any) (result ObrimCodecResponse) {
defer func() {
if recover() != nil {
result = ObrimCodecResponse{
Status: false,
Code: "FAILED_PANIC",
Payload: nil,
}
}
}()
if validation := obrimCodecValidation(operation, config); validation != nil {
return *validation
}
return obrimCodecDispatch(operation, config)
}
// Function Purpose: Validate required parameters, supported operations, formats, and parameter types.
func obrimCodecValidation(operation string, config map[string]any) *ObrimCodecResponse {
if operation != "encode" && operation != "decode" {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_UNSUPPORTED_OPERATION",
Payload: nil,
}
}
// Logic: Validate format.
formatValue, exists := config["format"]
if !exists {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_MISSING_FORMAT",
Payload: nil,
}
}
format, ok := formatValue.(string)
if !ok {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_FORMAT_TYPE",
Payload: nil,
}
}
switch format {
case "base8", "base10", "base16", "base32", "base64", "sqids":
default:
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_UNSUPPORTED_FORMAT",
Payload: nil,
}
}
// Logic: Validate data.
dataValue, exists := config["data"]
if !exists {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_MISSING_DATA",
Payload: nil,
}
}
switch dataValue.(type) {
case string, []byte:
default:
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_DATA_TYPE",
Payload: nil,
}
}
// Logic: Validate charset.
charsetValue, exists := config["charset"]
if !exists {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_MISSING_CHARSET",
Payload: nil,
}
}
if _, ok := charsetValue.(string); !ok {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_CHARSET_TYPE",
Payload: nil,
}
}
// Logic: Validate salt.
saltValue, exists := config["salt"]
if !exists {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_MISSING_SALT",
Payload: nil,
}
}
if _, ok := saltValue.(string); !ok {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_SALT_TYPE",
Payload: nil,
}
}
if format == "sqids" {
lengthValue, exists := config["length"]
if !exists {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_MISSING_LENGTH",
Payload: nil,
}
}
switch lengthValue.(type) {
case int, int8, int16, int32, int64:
default:
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_LENGTH_TYPE",
Payload: nil,
}
}
} else {
caseValue, exists := config["case"]
if !exists {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_MISSING_CASE",
Payload: nil,
}
}
caseRule, ok := caseValue.(string)
if !ok {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_CASE_TYPE",
Payload: nil,
}
}
if caseRule != "lower" && caseRule != "upper" {
return &ObrimCodecResponse{
Status: false,
Code: "FAILED_INVALID_CASE_VALUE",
Payload: nil,
}
}
}
return nil
}
// Function Purpose: Route validated requests to the appropriate codec implementation.
func obrimCodecDispatch(operation string, config map[string]any) ObrimCodecResponse {
format := config["format"].(string)
var (
// Logic:
value string
err error
)
switch operation {
case "encode":
switch format {
case "base8":
value, err = obrimCodecEncodeBase8(config)
case "base10":
value, err = obrimCodecEncodeBase10(config)
case "base16":
value, err = obrimCodecEncodeBase16(config)
case "base32":
value, err = obrimCodecEncodeBase32(config)
case "base64":
value, err = obrimCodecEncodeBase64(config)
case "sqids":
value, err = obrimCodecEncodeSqids(config)
}
case "decode":
switch format {
case "base8":
value, err = obrimCodecDecodeBase8(config)
case "base10":
value, err = obrimCodecDecodeBase10(config)
case "base16":
value, err = obrimCodecDecodeBase16(config)
case "base32":
value, err = obrimCodecDecodeBase32(config)
case "base64":
value, err = obrimCodecDecodeBase64(config)
case "sqids":
value, err = obrimCodecDecodeSqids(config)
}
}
if err != nil {
return ObrimCodecResponse{
Status: false,
Code: "FAILED_OPERATION",
Payload: nil,
}
}
return ObrimCodecResponse{
Status: true,
Code: "SUCCESS_OPERATION",
Payload: map[string]any{
"operation": operation,
"format": format,
"value": value,
},
}
}
// Function Purpose: Encode data using Base8 encoding.
func obrimCodecEncodeBase8(config map[string]any) (string, error) {
return obrimCodecEncodeRadix(config, 8)
}
// Function Purpose: Encode data using Base10 encoding.
func obrimCodecEncodeBase10(config map[string]any) (string, error) {
return obrimCodecEncodeRadix(config, 10)
}
// Function Purpose: Encode data using Base16 encoding.
func obrimCodecEncodeBase16(config map[string]any) (string, error) {
value := hex.EncodeToString(obrimCodecBytes(config["data"]))
return obrimCodecApplyCase(obrimCodecTransformEncode(value, config)), nil
}
// Function Purpose: Encode data using Base32 encoding.
func obrimCodecEncodeBase32(config map[string]any) (string, error) {
value := base32.StdEncoding.EncodeToString(obrimCodecBytes(config["data"]))
return obrimCodecApplyCase(obrimCodecTransformEncode(value, config)), nil
}
// Function Purpose: Encode data using Base64 encoding.
func obrimCodecEncodeBase64(config map[string]any) (string, error) {
value := base64.StdEncoding.EncodeToString(obrimCodecBytes(config["data"]))
return obrimCodecApplyCase(obrimCodecTransformEncode(value, config)), nil
}
// Function Purpose: Encode data using Sqids encoding with minimum length enforcement.
func obrimCodecEncodeSqids(config map[string]any) (string, error) {
length := obrimCodecLength(config["length"])
s, err := sqids.New(sqids.Options{
MinLength: uint8(length),
Alphabet: config["charset"].(string),
Blocklist: []string{},
})
if err != nil {
return "", err
}
number := obrimCodecBigInt(obrimCodecBytes(config["data"])).Uint64()
value, err := s.Encode([]uint64{number})
if err != nil {
return "", err
}
return obrimCodecTransformEncode(value, config), nil
}
// Function Purpose: Decode Base8 encoded data.
func obrimCodecDecodeBase8(config map[string]any) (string, error) {
return obrimCodecDecodeRadix(config, 8)
}
// Function Purpose: Decode Base10 encoded data.
func obrimCodecDecodeBase10(config map[string]any) (string, error) {
return obrimCodecDecodeRadix(config, 10)
}
// Function Purpose: Decode Base16 encoded data.
func obrimCodecDecodeBase16(config map[string]any) (string, error) {
value := obrimCodecTransformDecode(config["data"].(string), config)
decoded, err := hex.DecodeString(value)
if err != nil {
return "", err
}
return string(decoded), nil
}
// Function Purpose: Decode Base32 encoded data.
func obrimCodecDecodeBase32(config map[string]any) (string, error) {
value := obrimCodecTransformDecode(config["data"].(string), config)
decoded, err := base32.StdEncoding.DecodeString(strings.ToUpper(value))
if err != nil {
return "", err
}
return string(decoded), nil
}
// Function Purpose: Decode Base64 encoded data.
func obrimCodecDecodeBase64(config map[string]any) (string, error) {
value := obrimCodecTransformDecode(config["data"].(string), config)
decoded, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return "", err
}
return string(decoded), nil
}
// Function Purpose: Decode Sqids encoded data using deterministic length validation.
func obrimCodecDecodeSqids(config map[string]any) (string, error) {
length := obrimCodecLength(config["length"])
s, err := sqids.New(sqids.Options{
MinLength: uint8(length),
Alphabet: config["charset"].(string),
Blocklist: []string{},
})
if err != nil {
return "", err
}
value := obrimCodecTransformDecode(config["data"].(string), config)
numbers := s.Decode(value)
if len(numbers) == 0 {
return "", fmt.Errorf("decode failed")
}
return strconv.FormatUint(numbers[0], 10), nil
}
// Function Purpose: Encode generic radix values.
func obrimCodecEncodeRadix(config map[string]any, base int) (string, error) {
value := obrimCodecBigInt(obrimCodecBytes(config["data"])).Text(base)
value = obrimCodecTransformEncode(value, config)
value = obrimCodecApplyCase(value, config["case"].(string))
return value, nil
}
// Function Purpose: Decode generic radix values.
func obrimCodecDecodeRadix(config map[string]any, base int) (string, error) {
value := obrimCodecTransformDecode(config["data"].(string), config)
number := new(big.Int)
_, ok := number.SetString(value, base)
if !ok {
return "", fmt.Errorf("invalid value")
}
return string(number.Bytes()), nil
}
// Function Purpose: Convert supported data into bytes.
func obrimCodecBytes(data any) []byte {
switch value := data.(type) {
case string:
return []byte(value)
case []byte:
return value
default:
return []byte{}
}
}
// Function Purpose: Convert bytes into big integer.
func obrimCodecBigInt(data []byte) *big.Int {
return new(big.Int).SetBytes(data)
}
// Function Purpose: Apply encoding transformations.
func obrimCodecTransformEncode(value string, config map[string]any) string {
charset := config["charset"].(string)
salt := config["salt"].(string)
return salt + charset + value
}
// Function Purpose: Reverse encoding transformations.
func obrimCodecTransformDecode(value string, config map[string]any) string {
salt := config["salt"].(string)
charset := config["charset"].(string)
value = strings.TrimPrefix(value, salt)
value = strings.TrimPrefix(value, charset)
if caseRule, ok := config["case"].(string); ok {
if caseRule == "upper" {
value = strings.ToLower(value)
}
}
return value
}
// Function Purpose: Apply output casing rules.
func obrimCodecApplyCase(value string, rule string) string {
switch rule {
case "upper":
return strings.ToUpper(value)
default:
return strings.ToLower(value)
}
}
// Function Purpose: Convert supported length types into integer.
func obrimCodecLength(value any) int {
switch v := value.(type) {
case int:
return v
case int8:
return int(v)
case int16:
return int(v)
case int32:
return int(v)
case int64:
return int(v)
default:
return 0
}
}