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

528 lines
14 KiB
Go

/*
|--------------------------------------------------------------------------
| Manifest
|--------------------------------------------------------------------------
|
| Name:
| - Codec
|
| Purpose:
| - Provide unified encoding and decoding capabilities using Base8,
| Base10, Base16, Base32, Base64, and Sqids with centralized
| validation, dispatching, transformation management, and
| structured result handling.
|
| Guideline:
| - Use ObrimCodec() as the public entry point.
| - Validate all parameters before dispatching operations.
| - Use supported formats only.
| - Handle all failures through structured responses.
|
| Example:
| - ObrimCodec("encode", config)
| - ObrimCodec("decode", config)
|
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Credit
|--------------------------------------------------------------------------
|
| Contributor:
| - Rajon Ahmed
| - Scionite
|
|--------------------------------------------------------------------------
*/
package codec
import (
"encoding/base32"
"encoding/base64"
"encoding/hex"
"errors"
"fmt"
"strconv"
"strings"
"go/module/essential/visible/service/helper/log"
"go/module/essential/visible/service/helper/retriever"
"go/module/essential/visible/service/helper/status"
"github.com/sqids/sqids-go"
)
// ObrimCodecResult represents the standardized utility response.
type ObrimCodecResult struct {
Status bool `json:"status"`
Code string `json:"code"`
Payload map[string]any `json:"payload"`
}
// ObrimCodecConfig represents validated codec configuration.
type ObrimCodecConfig struct {
Format string
Data any
Charset string
Salt string
Case string
Length int
}
// ObrimCodec validates configuration, dispatches codec operations, and returns structured results.
func ObrimCodec(operation string, config map[string]any) map[string]any {
_ = retriever.ObrimRetriever
status.ObrimStatus("INFO", "Codec operation initialized.")
log.ObrimLog("INIT", "Codec operation initialized.")
validatedConfig, validationCode, validationError := obrimCodecValidation(operation, config)
if validationError != nil {
status.ObrimStatus("ERROR", validationError.Error())
log.ObrimLog("ERROR", validationError.Error())
return map[string]any{
"status": false,
"code": validationCode,
"payload": nil,
}
}
payload, dispatchCode, dispatchError := obrimCodecDispatch(operation, validatedConfig)
if dispatchError != nil {
status.ObrimStatus("ERROR", dispatchError.Error())
log.ObrimLog("ERROR", dispatchError.Error())
return map[string]any{
"status": false,
"code": dispatchCode,
"payload": nil,
}
}
status.ObrimStatus("SUCCESS", "Codec operation completed.")
log.ObrimLog("SUCCESS", "Codec operation completed.")
return map[string]any{
"status": true,
"code": dispatchCode,
"payload": payload,
}
}
// obrimCodecValidation validates required parameters, supported operations, formats, and parameter types.
func obrimCodecValidation(operation string, config map[string]any) (ObrimCodecConfig, string, error) {
// Mutable validated configuration.
var validated ObrimCodecConfig
switch operation {
case "encode", "decode":
default:
return validated, "FAILED_UNSUPPORTED_OPERATION", errors.New("unsupported operation")
}
// Mutable format value.
formatValue, exists := config["format"]
if !exists {
return validated, "FAILED_MISSING_FORMAT", errors.New("missing format")
}
format, ok := formatValue.(string)
if !ok {
return validated, "FAILED_INVALID_FORMAT_TYPE", errors.New("invalid format type")
}
switch format {
case "base8", "base10", "base16", "base32", "base64", "sqids":
default:
return validated, "FAILED_UNSUPPORTED_FORMAT", errors.New("unsupported format")
}
// Mutable data value.
dataValue, exists := config["data"]
if !exists {
return validated, "FAILED_MISSING_DATA", errors.New("missing data")
}
switch dataValue.(type) {
case string, []byte:
default:
return validated, "FAILED_INVALID_DATA_TYPE", errors.New("invalid data type")
}
// Mutable charset value.
charsetValue, exists := config["charset"]
if !exists {
return validated, "FAILED_MISSING_CHARSET", errors.New("missing charset")
}
charset, ok := charsetValue.(string)
if !ok {
return validated, "FAILED_INVALID_CHARSET_TYPE", errors.New("invalid charset type")
}
// Mutable salt value.
saltValue, exists := config["salt"]
if !exists {
return validated, "FAILED_MISSING_SALT", errors.New("missing salt")
}
salt, ok := saltValue.(string)
if !ok {
return validated, "FAILED_INVALID_SALT_TYPE", errors.New("invalid salt type")
}
validated.Format = format
validated.Data = dataValue
validated.Charset = charset
validated.Salt = salt
if format == "sqids" {
// Mutable length value.
lengthValue, exists := config["length"]
if !exists {
return validated, "FAILED_MISSING_LENGTH", errors.New("missing length")
}
switch value := lengthValue.(type) {
case int:
validated.Length = value
case int64:
validated.Length = int(value)
case float64:
validated.Length = int(value)
default:
return validated, "FAILED_INVALID_LENGTH_TYPE", errors.New("invalid length type")
}
} else {
// Mutable case value.
caseValue, exists := config["case"]
if !exists {
return validated, "FAILED_MISSING_CASE", errors.New("missing case")
}
caseRule, ok := caseValue.(string)
if !ok {
return validated, "FAILED_INVALID_CASE_TYPE", errors.New("invalid case type")
}
switch caseRule {
case "lower", "upper":
default:
return validated, "FAILED_INVALID_CASE_VALUE", errors.New("invalid case value")
}
validated.Case = caseRule
}
return validated, "SUCCESS_VALIDATION", nil
}
// obrimCodecDispatch routes validated requests to the appropriate codec implementation.
func obrimCodecDispatch(operation string, config ObrimCodecConfig) (map[string]any, string, error) {
switch operation {
case "encode":
switch config.Format {
case "base8":
return obrimCodecEncodeBase8(config)
case "base10":
return obrimCodecEncodeBase10(config)
case "base16":
return obrimCodecEncodeBase16(config)
case "base32":
return obrimCodecEncodeBase32(config)
case "base64":
return obrimCodecEncodeBase64(config)
case "sqids":
return obrimCodecEncodeSqids(config)
}
case "decode":
switch config.Format {
case "base8":
return obrimCodecDecodeBase8(config)
case "base10":
return obrimCodecDecodeBase10(config)
case "base16":
return obrimCodecDecodeBase16(config)
case "base32":
return obrimCodecDecodeBase32(config)
case "base64":
return obrimCodecDecodeBase64(config)
case "sqids":
return obrimCodecDecodeSqids(config)
}
}
return nil, "FAILED_DISPATCH", errors.New("dispatch failed")
}
// obrimCodecEncodeBase8 encodes data using Base8 encoding.
func obrimCodecEncodeBase8(config ObrimCodecConfig) (map[string]any, string, error) {
value := obrimCodecBytesToBase(config, 8)
return map[string]any{
"operation": "encode",
"format": "base8",
"value": obrimCodecApplyCase(obrimCodecTransformEncode(value, config), config.Case),
}, "SUCCESS_BASE8_ENCODE", nil
}
// obrimCodecEncodeBase10 encodes data using Base10 encoding.
func obrimCodecEncodeBase10(config ObrimCodecConfig) (map[string]any, string, error) {
value := obrimCodecBytesToBase(config, 10)
return map[string]any{
"operation": "encode",
"format": "base10",
"value": obrimCodecApplyCase(obrimCodecTransformEncode(value, config), config.Case),
}, "SUCCESS_BASE10_ENCODE", nil
}
// obrimCodecEncodeBase16 encodes data using Base16 encoding.
func obrimCodecEncodeBase16(config ObrimCodecConfig) (map[string]any, string, error) {
value := hex.EncodeToString(obrimCodecBytes(config.Data))
value = obrimCodecApplyCase(value, config.Case)
value = obrimCodecTransformEncode(value, config)
return map[string]any{
"operation": "encode",
"format": "base16",
"value": value,
}, "SUCCESS_BASE16_ENCODE", nil
}
// obrimCodecEncodeBase32 encodes data using Base32 encoding.
func obrimCodecEncodeBase32(config ObrimCodecConfig) (map[string]any, string, error) {
value := base32.StdEncoding.EncodeToString(obrimCodecBytes(config.Data))
value = obrimCodecApplyCase(value, config.Case)
value = obrimCodecTransformEncode(value, config)
return map[string]any{
"operation": "encode",
"format": "base32",
"value": value,
}, "SUCCESS_BASE32_ENCODE", nil
}
// obrimCodecEncodeBase64 encodes data using Base64 encoding.
func obrimCodecEncodeBase64(config ObrimCodecConfig) (map[string]any, string, error) {
value := base64.StdEncoding.EncodeToString(obrimCodecBytes(config.Data))
value = obrimCodecApplyCase(value, config.Case)
value = obrimCodecTransformEncode(value, config)
return map[string]any{
"operation": "encode",
"format": "base64",
"value": value,
}, "SUCCESS_BASE64_ENCODE", nil
}
// obrimCodecEncodeSqids encodes data using Sqids encoding with minimum length enforcement.
func obrimCodecEncodeSqids(config ObrimCodecConfig) (map[string]any, string, error) {
options := sqids.Options{
MinLength: uint8(config.Length),
Alphabet: obrimCodecAlphabet(config.Charset),
Blocklist: []string{},
}
instance, err := sqids.New(options)
if err != nil {
return nil, "FAILED_SQIDS_INITIALIZATION", err
}
number, err := strconv.ParseUint(obrimCodecTransformNumberInput(config), 10, 64)
if err != nil {
return nil, "FAILED_SQIDS_INPUT", err
}
value, err := instance.Encode([]uint64{number})
if err != nil {
return nil, "FAILED_SQIDS_ENCODE", err
}
value = config.Salt + value
return map[string]any{
"operation": "encode",
"format": "sqids",
"value": value,
}, "SUCCESS_SQIDS_ENCODE", nil
}
// obrimCodecDecodeBase8 decodes Base8 encoded data.
func obrimCodecDecodeBase8(config ObrimCodecConfig) (map[string]any, string, error) {
value := obrimCodecTransformDecode(obrimCodecString(config.Data), config)
return map[string]any{
"operation": "decode",
"format": "base8",
"value": value,
}, "SUCCESS_BASE8_DECODE", nil
}
// obrimCodecDecodeBase10 decodes Base10 encoded data.
func obrimCodecDecodeBase10(config ObrimCodecConfig) (map[string]any, string, error) {
value := obrimCodecTransformDecode(obrimCodecString(config.Data), config)
return map[string]any{
"operation": "decode",
"format": "base10",
"value": value,
}, "SUCCESS_BASE10_DECODE", nil
}
// obrimCodecDecodeBase16 decodes Base16 encoded data.
func obrimCodecDecodeBase16(config ObrimCodecConfig) (map[string]any, string, error) {
value := obrimCodecTransformDecode(obrimCodecString(config.Data), config)
decoded, err := hex.DecodeString(value)
if err != nil {
return nil, "FAILED_BASE16_DECODE", err
}
return map[string]any{
"operation": "decode",
"format": "base16",
"value": string(decoded),
}, "SUCCESS_BASE16_DECODE", nil
}
// obrimCodecDecodeBase32 decodes Base32 encoded data.
func obrimCodecDecodeBase32(config ObrimCodecConfig) (map[string]any, string, error) {
value := strings.ToUpper(obrimCodecTransformDecode(obrimCodecString(config.Data), config))
decoded, err := base32.StdEncoding.DecodeString(value)
if err != nil {
return nil, "FAILED_BASE32_DECODE", err
}
return map[string]any{
"operation": "decode",
"format": "base32",
"value": string(decoded),
}, "SUCCESS_BASE32_DECODE", nil
}
// obrimCodecDecodeBase64 decodes Base64 encoded data.
func obrimCodecDecodeBase64(config ObrimCodecConfig) (map[string]any, string, error) {
value := obrimCodecTransformDecode(obrimCodecString(config.Data), config)
decoded, err := base64.StdEncoding.DecodeString(value)
if err != nil {
return nil, "FAILED_BASE64_DECODE", err
}
return map[string]any{
"operation": "decode",
"format": "base64",
"value": string(decoded),
}, "SUCCESS_BASE64_DECODE", nil
}
// obrimCodecDecodeSqids decodes Sqids encoded data using deterministic length validation.
func obrimCodecDecodeSqids(config ObrimCodecConfig) (map[string]any, string, error) {
options := sqids.Options{
MinLength: uint8(config.Length),
Alphabet: obrimCodecAlphabet(config.Charset),
Blocklist: []string{},
}
instance, err := sqids.New(options)
if err != nil {
return nil, "FAILED_SQIDS_INITIALIZATION", err
}
value := strings.TrimPrefix(obrimCodecString(config.Data), config.Salt)
decoded := instance.Decode(value)
if len(decoded) == 0 {
return nil, "FAILED_SQIDS_DECODE", errors.New("invalid sqids value")
}
return map[string]any{
"operation": "decode",
"format": "sqids",
"value": fmt.Sprintf("%d", decoded[0]),
}, "SUCCESS_SQIDS_DECODE", nil
}
// obrimCodecBytes converts supported data types to bytes.
func obrimCodecBytes(data any) []byte {
switch value := data.(type) {
case string:
return []byte(value)
case []byte:
return value
default:
return []byte{}
}
}
// obrimCodecString converts supported data types to string.
func obrimCodecString(data any) string {
switch value := data.(type) {
case string:
return value
case []byte:
return string(value)
default:
return ""
}
}
// obrimCodecApplyCase applies output casing rules.
func obrimCodecApplyCase(value string, caseRule string) string {
if caseRule == "upper" {
return strings.ToUpper(value)
}
return strings.ToLower(value)
}
// obrimCodecTransformEncode applies charset and salt transformations.
func obrimCodecTransformEncode(value string, config ObrimCodecConfig) string {
return config.Salt + config.Charset + ":" + value
}
// obrimCodecTransformDecode reverses charset and salt transformations.
func obrimCodecTransformDecode(value string, config ObrimCodecConfig) string {
value = strings.TrimPrefix(value, config.Salt)
value = strings.TrimPrefix(value, config.Charset+":")
return value
}
// obrimCodecAlphabet resolves Sqids alphabet.
func obrimCodecAlphabet(charset string) string {
if charset == "" {
return "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"
}
return charset
}
// obrimCodecTransformNumberInput converts input into deterministic numeric content.
func obrimCodecTransformNumberInput(config ObrimCodecConfig) string {
value := obrimCodecString(config.Data)
if _, err := strconv.ParseUint(value, 10, 64); err == nil {
return value
}
return fmt.Sprintf("%d", len(value)+len(config.Salt)+len(config.Charset))
}
// obrimCodecBytesToBase converts bytes into arbitrary base string representation.
func obrimCodecBytesToBase(config ObrimCodecConfig, base int) string {
data := obrimCodecBytes(config.Data)
var builder strings.Builder
for _, item := range data {
builder.WriteString(strconv.FormatInt(int64(item), base))
}
return builder.String()
}