diff --git a/essential/visible/service/helper/cipher/cipher.go b/essential/visible/service/helper/cipher/cipher.go index e69de29..68886a1 100644 --- a/essential/visible/service/helper/cipher/cipher.go +++ b/essential/visible/service/helper/cipher/cipher.go @@ -0,0 +1,430 @@ +package cipher + +import ( + "bytes" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "encoding/json" + "errors" + "io" + "os" + "path/filepath" +) + +const ( + obrimCipherAlgorithm = "AES-256" + obrimCipherSignature = "OBRIMCIPHER" + obrimCipherVersion = "1" + obrimCipherSaltSize = 32 + obrimCipherNonceSize = 12 +) + +type ObrimCipherConfig struct { + Key string `json:"key"` + Input string `json:"input"` + Output string `json:"output"` +} + +type obrimCipherResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload interface{} `json:"payload"` +} + +type obrimCipherStandardPayload struct { + Operation string `json:"operation"` + Algorithm string `json:"algorithm"` + InputPath string `json:"inputPath"` + OutputPath string `json:"outputPath"` +} + +type obrimCipherSaltedPayload struct { + Operation string `json:"operation"` + Algorithm string `json:"algorithm"` + Format string `json:"format"` + Version string `json:"version"` + InputPath string `json:"inputPath"` + OutputPath string `json:"outputPath"` +} + +// ObrimCipher executes encryption or decryption workflows. +func ObrimCipher(operationType string, config ObrimCipherConfig) ([]byte, error) { + if err := obrimCipherValidateType(operationType); err != nil { + return obrimCipherBuildResponse(false, "FAILED_INVALID_TYPE", nil) + } + + if err := obrimCipherValidateConfig(config); err != nil { + return obrimCipherBuildResponse(false, "FAILED_INVALID_CONFIG", nil) + } + + if err := obrimCipherValidateKey(config.Key); err != nil { + return obrimCipherBuildResponse(false, "FAILED_INVALID_KEY", nil) + } + + inputPath, err := obrimCipherResolvePath(config.Input) + if err != nil { + return obrimCipherBuildResponse(false, "FAILED_INVALID_INPUT_PATH", nil) + } + + outputPath, err := obrimCipherResolvePath(config.Output) + if err != nil { + return obrimCipherBuildResponse(false, "FAILED_INVALID_OUTPUT_PATH", nil) + } + + inputData, err := obrimCipherReadInput(inputPath) + if err != nil { + return obrimCipherBuildResponse(false, "FAILED_INPUT_ACCESS", nil) + } + + var outputData []byte + + switch operationType { + case "encrypt-standard": + outputData, err = obrimCipherEncryptStandard(inputData, config.Key) + + case "encrypt-salted": + outputData, err = obrimCipherEncryptSalted(inputData, config.Key) + + case "decrypt-standard": + outputData, err = obrimCipherDecryptStandard(inputData, config.Key) + + case "decrypt-salted": + outputData, err = obrimCipherDecryptSalted(inputData, config.Key) + } + + if err != nil { + switch operationType { + case "encrypt-standard", "encrypt-salted": + return obrimCipherBuildResponse(false, "FAILED_ENCRYPTION", nil) + case "decrypt-standard", "decrypt-salted": + return obrimCipherBuildResponse(false, "FAILED_DECRYPTION", nil) + } + } + + if err := obrimCipherWriteOutput(outputPath, outputData); err != nil { + return obrimCipherBuildResponse(false, "FAILED_OUTPUT_WRITE", nil) + } + + switch operationType { + case "encrypt-standard": + return obrimCipherBuildResponse( + true, + "SUCCESS_ENCRYPT_STANDARD", + obrimCipherStandardPayload{ + Operation: operationType, + Algorithm: obrimCipherAlgorithm, + InputPath: inputPath, + OutputPath: outputPath, + }, + ) + + case "decrypt-standard": + return obrimCipherBuildResponse( + true, + "SUCCESS_DECRYPT_STANDARD", + obrimCipherStandardPayload{ + Operation: operationType, + Algorithm: obrimCipherAlgorithm, + InputPath: inputPath, + OutputPath: outputPath, + }, + ) + + case "encrypt-salted": + return obrimCipherBuildResponse( + true, + "SUCCESS_ENCRYPT_SALTED", + obrimCipherSaltedPayload{ + Operation: operationType, + Algorithm: obrimCipherAlgorithm, + Format: obrimCipherSignature, + Version: obrimCipherVersion, + InputPath: inputPath, + OutputPath: outputPath, + }, + ) + + case "decrypt-salted": + return obrimCipherBuildResponse( + true, + "SUCCESS_DECRYPT_SALTED", + obrimCipherSaltedPayload{ + Operation: operationType, + Algorithm: obrimCipherAlgorithm, + Format: obrimCipherSignature, + Version: obrimCipherVersion, + InputPath: inputPath, + OutputPath: outputPath, + }, + ) + } + + return obrimCipherBuildResponse(false, "FAILED_UNKNOWN_OPERATION", nil) +} + +// obrimCipherValidateType validates operation type. +func obrimCipherValidateType(operationType string) error { + switch operationType { + case "encrypt-standard", + "encrypt-salted", + "decrypt-standard", + "decrypt-salted": + return nil + default: + return errors.New("invalid operation type") + } +} + +// obrimCipherValidateConfig validates configuration values. +func obrimCipherValidateConfig(config ObrimCipherConfig) error { + if config.Key == "" { + return errors.New("missing key") + } + + if config.Input == "" { + return errors.New("missing input") + } + + if config.Output == "" { + return errors.New("missing output") + } + + return nil +} + +// obrimCipherValidateKey validates AES-256 key requirements. +func obrimCipherValidateKey(key string) error { + if len([]byte(key)) != 32 { + return errors.New("invalid aes-256 key") + } + + return nil +} + +// obrimCipherResolvePath resolves paths to absolute paths. +func obrimCipherResolvePath(path string) (string, error) { + return filepath.Abs(path) +} + +// obrimCipherReadInput reads source file content. +func obrimCipherReadInput(path string) ([]byte, error) { + return os.ReadFile(path) +} + +// obrimCipherWriteOutput writes processed output. +func obrimCipherWriteOutput(path string, data []byte) error { + directory := filepath.Dir(path) + + if err := os.MkdirAll(directory, 0755); err != nil { + return err + } + + tempFile, err := os.CreateTemp(directory, ".obrim-cipher-*") + if err != nil { + return err + } + + tempPath := tempFile.Name() + + defer func() { + _ = tempFile.Close() + }() + + if _, err := tempFile.Write(data); err != nil { + _ = os.Remove(tempPath) + return err + } + + if err := tempFile.Sync(); err != nil { + _ = os.Remove(tempPath) + return err + } + + if err := tempFile.Close(); err != nil { + _ = os.Remove(tempPath) + return err + } + + return os.Rename(tempPath, path) +} + +// obrimCipherBuildResponse builds standardized responses. +func obrimCipherBuildResponse(status bool, code string, payload interface{}) ([]byte, error) { + response := obrimCipherResponse{ + Status: status, + Code: code, + Payload: payload, + } + + result, err := json.Marshal(response) + if err != nil { + return nil, err + } + + return result, nil +} + +// obrimCipherEncryptStandard encrypts data without salt metadata. +func obrimCipherEncryptStandard(data []byte, key string) ([]byte, error) { + return obrimCipherEncryptAES(data, []byte(key)) +} + +// obrimCipherGenerateSalt generates cryptographically secure salt. +func obrimCipherGenerateSalt() ([]byte, error) { + salt := make([]byte, obrimCipherSaltSize) + + if _, err := io.ReadFull(rand.Reader, salt); err != nil { + return nil, err + } + + return salt, nil +} + +// obrimCipherBuildHeader builds encrypted file header. +func obrimCipherBuildHeader() []byte { + var buffer bytes.Buffer + + buffer.WriteString(obrimCipherSignature) + buffer.WriteByte('|') + buffer.WriteString(obrimCipherVersion) + buffer.WriteByte('|') + + return buffer.Bytes() +} + +// obrimCipherEncryptSalted encrypts data with embedded salt metadata. +func obrimCipherEncryptSalted(data []byte, key string) ([]byte, error) { + salt, err := obrimCipherGenerateSalt() + if err != nil { + return nil, err + } + + derivedKey := sha256.Sum256(append([]byte(key), salt...)) + + encrypted, err := obrimCipherEncryptAES(data, derivedKey[:]) + if err != nil { + return nil, err + } + + header := obrimCipherBuildHeader() + + result := make([]byte, 0, len(header)+len(salt)+len(encrypted)) + result = append(result, header...) + result = append(result, salt...) + result = append(result, encrypted...) + + return result, nil +} + +// obrimCipherDecryptStandard decrypts AES-256 encrypted data. +func obrimCipherDecryptStandard(data []byte, key string) ([]byte, error) { + return obrimCipherDecryptAES(data, []byte(key)) +} + +// obrimCipherParseHeader parses encrypted metadata. +func obrimCipherParseHeader(data []byte) (string, string, int, error) { + header := []byte(obrimCipherSignature + "|" + obrimCipherVersion + "|") + + if len(data) < len(header) { + return "", "", 0, errors.New("invalid header") + } + + if !bytes.Equal(data[:len(header)], header) { + return "", "", 0, errors.New("invalid header") + } + + return obrimCipherSignature, obrimCipherVersion, len(header), nil +} + +// obrimCipherExtractSalt extracts embedded salt. +func obrimCipherExtractSalt(data []byte, offset int) ([]byte, error) { + if len(data) < offset+obrimCipherSaltSize { + return nil, errors.New("salt extraction failed") + } + + return data[offset : offset+obrimCipherSaltSize], nil +} + +// obrimCipherExtractPayload extracts encrypted payload. +func obrimCipherExtractPayload(data []byte, offset int) ([]byte, error) { + start := offset + obrimCipherSaltSize + + if len(data) <= start { + return nil, errors.New("payload extraction failed") + } + + return data[start:], nil +} + +// obrimCipherDecryptSalted decrypts AES-256 encrypted data with salt metadata. +func obrimCipherDecryptSalted(data []byte, key string) ([]byte, error) { + _, _, offset, err := obrimCipherParseHeader(data) + if err != nil { + return nil, err + } + + salt, err := obrimCipherExtractSalt(data, offset) + if err != nil { + return nil, err + } + + payload, err := obrimCipherExtractPayload(data, offset) + if err != nil { + return nil, err + } + + derivedKey := sha256.Sum256(append([]byte(key), salt...)) + + return obrimCipherDecryptAES(payload, derivedKey[:]) +} + +// obrimCipherEncryptAES performs AES-256 GCM encryption. +func obrimCipherEncryptAES(data []byte, key []byte) ([]byte, error) { + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonce := make([]byte, obrimCipherNonceSize) + + if _, err := io.ReadFull(rand.Reader, nonce); err != nil { + return nil, err + } + + encrypted := gcm.Seal(nil, nonce, data, nil) + + result := make([]byte, 0, len(nonce)+len(encrypted)) + result = append(result, nonce...) + result = append(result, encrypted...) + + return result, nil +} + +// obrimCipherDecryptAES performs AES-256 GCM decryption. +func obrimCipherDecryptAES(data []byte, key []byte) ([]byte, error) { + if len(data) < obrimCipherNonceSize { + return nil, errors.New("invalid encrypted payload") + } + + block, err := aes.NewCipher(key) + if err != nil { + return nil, err + } + + gcm, err := cipher.NewGCM(block) + if err != nil { + return nil, err + } + + nonce := data[:obrimCipherNonceSize] + payload := data[obrimCipherNonceSize:] + + return gcm.Open(nil, nonce, payload, nil) +} diff --git a/essential/visible/service/helper/codec/codec.go b/essential/visible/service/helper/codec/codec.go index e69de29..8cab8ba 100644 --- a/essential/visible/service/helper/codec/codec.go +++ b/essential/visible/service/helper/codec/codec.go @@ -0,0 +1,433 @@ +package codec + +import ( + "encoding/base32" + "encoding/base64" + "encoding/hex" + "errors" + "fmt" + "math/big" + "strconv" + "strings" + + "github.com/sqids/sqids-go" +) + +const ( + obrimCodecOperationEncode = "encode" + obrimCodecOperationDecode = "decode" + + obrimCodecFormatBase8 = "base8" + obrimCodecFormatBase10 = "base10" + obrimCodecFormatBase16 = "base16" + obrimCodecFormatBase32 = "base32" + obrimCodecFormatBase64 = "base64" + obrimCodecFormatSqids = "sqids" + + obrimCodecCaseLower = "lower" + obrimCodecCaseUpper = "upper" +) + +type obrimCodecConfiguration struct { + Format string + Data interface{} + Charset string + Salt string + Case string + Length int +} + +type obrimCodecResult struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload interface{} `json:"payload"` +} + +type obrimCodecPayload struct { + Operation string `json:"operation"` + Format string `json:"format"` + Value string `json:"value"` +} + +// ObrimCodec validates configuration, dispatches codec operations, and returns structured results. +func ObrimCodec(operation string, config map[string]interface{}) map[string]interface{} { + validatedConfiguration, validationError := obrimCodecValidation(operation, config) + if validationError != nil { + return map[string]interface{}{ + "status": false, + "code": "FAILED_VALIDATION", + "payload": nil, + } + } + + value, dispatchError := obrimCodecDispatch(operation, validatedConfiguration) + if dispatchError != nil { + return map[string]interface{}{ + "status": false, + "code": "FAILED_OPERATION", + "payload": nil, + } + } + + return map[string]interface{}{ + "status": true, + "code": "SUCCESS_OPERATION", + "payload": map[string]interface{}{ + "operation": operation, + "format": validatedConfiguration.Format, + "value": value, + }, + } +} + +// obrimCodecValidation validates required parameters, supported operations, formats, and parameter types. +func obrimCodecValidation(operation string, config map[string]interface{}) (*obrimCodecConfiguration, error) { + switch operation { + case obrimCodecOperationEncode, obrimCodecOperationDecode: + default: + return nil, errors.New("unsupported operation") + } + + formatValue, exists := config["format"] + if !exists { + return nil, errors.New("missing format") + } + + format, ok := formatValue.(string) + if !ok { + return nil, errors.New("invalid format type") + } + + switch format { + case obrimCodecFormatBase8, + obrimCodecFormatBase10, + obrimCodecFormatBase16, + obrimCodecFormatBase32, + obrimCodecFormatBase64, + obrimCodecFormatSqids: + default: + return nil, errors.New("unsupported format") + } + + data, exists := config["data"] + if !exists { + return nil, errors.New("missing data") + } + + charsetValue, exists := config["charset"] + if !exists { + return nil, errors.New("missing charset") + } + + charset, ok := charsetValue.(string) + if !ok { + return nil, errors.New("invalid charset type") + } + + saltValue, exists := config["salt"] + if !exists { + return nil, errors.New("missing salt") + } + + salt, ok := saltValue.(string) + if !ok { + return nil, errors.New("invalid salt type") + } + + configuration := &obrimCodecConfiguration{ + Format: format, + Data: data, + Charset: charset, + Salt: salt, + } + + if format == obrimCodecFormatSqids { + lengthValue, exists := config["length"] + if !exists { + return nil, errors.New("missing length") + } + + switch value := lengthValue.(type) { + case int: + configuration.Length = value + case float64: + configuration.Length = int(value) + default: + return nil, errors.New("invalid length type") + } + } else { + caseValue, exists := config["case"] + if !exists { + return nil, errors.New("missing case") + } + + caseRule, ok := caseValue.(string) + if !ok { + return nil, errors.New("invalid case type") + } + + switch caseRule { + case obrimCodecCaseLower, obrimCodecCaseUpper: + default: + return nil, errors.New("invalid case value") + } + + configuration.Case = caseRule + } + + switch data.(type) { + case string, []byte: + default: + return nil, errors.New("invalid data type") + } + + return configuration, nil +} + +// obrimCodecDispatch routes validated requests to the appropriate codec implementation. +func obrimCodecDispatch(operation string, configuration *obrimCodecConfiguration) (string, error) { + switch operation { + case obrimCodecOperationEncode: + switch configuration.Format { + case obrimCodecFormatBase8: + return obrimCodecEncodeBase8(configuration) + case obrimCodecFormatBase10: + return obrimCodecEncodeBase10(configuration) + case obrimCodecFormatBase16: + return obrimCodecEncodeBase16(configuration) + case obrimCodecFormatBase32: + return obrimCodecEncodeBase32(configuration) + case obrimCodecFormatBase64: + return obrimCodecEncodeBase64(configuration) + case obrimCodecFormatSqids: + return obrimCodecEncodeSqids(configuration) + } + case obrimCodecOperationDecode: + switch configuration.Format { + case obrimCodecFormatBase8: + return obrimCodecDecodeBase8(configuration) + case obrimCodecFormatBase10: + return obrimCodecDecodeBase10(configuration) + case obrimCodecFormatBase16: + return obrimCodecDecodeBase16(configuration) + case obrimCodecFormatBase32: + return obrimCodecDecodeBase32(configuration) + case obrimCodecFormatBase64: + return obrimCodecDecodeBase64(configuration) + case obrimCodecFormatSqids: + return obrimCodecDecodeSqids(configuration) + } + } + + return "", errors.New("dispatch failure") +} + +// obrimCodecNormalizeInput converts supported input types into string form. +func obrimCodecNormalizeInput(data interface{}) string { + switch value := data.(type) { + case string: + return value + case []byte: + return string(value) + default: + return "" + } +} + +// obrimCodecApplyTransform applies charset and salt transformations. +func obrimCodecApplyTransform(value string, charset string, salt string) string { + return charset + salt + value +} + +// obrimCodecReverseTransform reverses charset and salt transformations. +func obrimCodecReverseTransform(value string, charset string, salt string) string { + prefix := charset + salt + return strings.TrimPrefix(value, prefix) +} + +// obrimCodecApplyCase applies output casing. +func obrimCodecApplyCase(value string, rule string) string { + switch rule { + case obrimCodecCaseUpper: + return strings.ToUpper(value) + default: + return strings.ToLower(value) + } +} + +// obrimCodecEncodeBase8 encodes data using Base8 encoding. +func obrimCodecEncodeBase8(configuration *obrimCodecConfiguration) (string, error) { + input := obrimCodecApplyTransform( + obrimCodecNormalizeInput(configuration.Data), + configuration.Charset, + configuration.Salt, + ) + + var builder strings.Builder + + for _, character := range []byte(input) { + builder.WriteString(fmt.Sprintf("%03o", character)) + } + + return obrimCodecApplyCase(builder.String(), configuration.Case), nil +} + +// obrimCodecDecodeBase8 decodes Base8 encoded data. +func obrimCodecDecodeBase8(configuration *obrimCodecConfiguration) (string, error) { + input := strings.ToLower(obrimCodecNormalizeInput(configuration.Data)) + + if len(input)%3 != 0 { + return "", errors.New("invalid base8 data") + } + + decoded := make([]byte, 0) + + for index := 0; index < len(input); index += 3 { + value, errorValue := strconv.ParseUint(input[index:index+3], 8, 8) + if errorValue != nil { + return "", errorValue + } + + decoded = append(decoded, byte(value)) + } + + return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil +} + +// obrimCodecEncodeBase10 encodes data using Base10 encoding. +func obrimCodecEncodeBase10(configuration *obrimCodecConfiguration) (string, error) { + input := obrimCodecApplyTransform( + obrimCodecNormalizeInput(configuration.Data), + configuration.Charset, + configuration.Salt, + ) + + value := new(big.Int).SetBytes([]byte(input)) + return obrimCodecApplyCase(value.Text(10), configuration.Case), nil +} + +// obrimCodecDecodeBase10 decodes Base10 encoded data. +func obrimCodecDecodeBase10(configuration *obrimCodecConfiguration) (string, error) { + value := new(big.Int) + + if _, success := value.SetString(obrimCodecNormalizeInput(configuration.Data), 10); !success { + return "", errors.New("invalid base10 data") + } + + return obrimCodecReverseTransform(string(value.Bytes()), configuration.Charset, configuration.Salt), nil +} + +// obrimCodecEncodeBase16 encodes data using Base16 encoding. +func obrimCodecEncodeBase16(configuration *obrimCodecConfiguration) (string, error) { + input := obrimCodecApplyTransform( + obrimCodecNormalizeInput(configuration.Data), + configuration.Charset, + configuration.Salt, + ) + + output := hex.EncodeToString([]byte(input)) + return obrimCodecApplyCase(output, configuration.Case), nil +} + +// obrimCodecDecodeBase16 decodes Base16 encoded data. +func obrimCodecDecodeBase16(configuration *obrimCodecConfiguration) (string, error) { + decoded, err := hex.DecodeString(obrimCodecNormalizeInput(configuration.Data)) + if err != nil { + return "", err + } + + return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil +} + +// obrimCodecEncodeBase32 encodes data using Base32 encoding. +func obrimCodecEncodeBase32(configuration *obrimCodecConfiguration) (string, error) { + input := obrimCodecApplyTransform( + obrimCodecNormalizeInput(configuration.Data), + configuration.Charset, + configuration.Salt, + ) + + output := base32.StdEncoding.EncodeToString([]byte(input)) + return obrimCodecApplyCase(output, configuration.Case), nil +} + +// obrimCodecDecodeBase32 decodes Base32 encoded data. +func obrimCodecDecodeBase32(configuration *obrimCodecConfiguration) (string, error) { + decoded, err := base32.StdEncoding.DecodeString(strings.ToUpper(obrimCodecNormalizeInput(configuration.Data))) + if err != nil { + return "", err + } + + return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil +} + +// obrimCodecEncodeBase64 encodes data using Base64 encoding. +func obrimCodecEncodeBase64(configuration *obrimCodecConfiguration) (string, error) { + input := obrimCodecApplyTransform( + obrimCodecNormalizeInput(configuration.Data), + configuration.Charset, + configuration.Salt, + ) + + output := base64.StdEncoding.EncodeToString([]byte(input)) + return obrimCodecApplyCase(output, configuration.Case), nil +} + +// obrimCodecDecodeBase64 decodes Base64 encoded data. +func obrimCodecDecodeBase64(configuration *obrimCodecConfiguration) (string, error) { + decoded, err := base64.StdEncoding.DecodeString(obrimCodecNormalizeInput(configuration.Data)) + if err != nil { + return "", err + } + + return obrimCodecReverseTransform(string(decoded), configuration.Charset, configuration.Salt), nil +} + +// obrimCodecEncodeSqids encodes data using Sqids encoding with minimum length enforcement. +func obrimCodecEncodeSqids(configuration *obrimCodecConfiguration) (string, error) { + input := obrimCodecApplyTransform( + obrimCodecNormalizeInput(configuration.Data), + configuration.Charset, + configuration.Salt, + ) + + numbers := make([]uint64, 0) + + for _, value := range []byte(input) { + numbers = append(numbers, uint64(value)) + } + + sqidsInstance, err := sqids.New(sqids.Options{ + Alphabet: configuration.Charset, + MinLength: uint8(configuration.Length), + }) + if err != nil { + return "", err + } + + return sqidsInstance.Encode(numbers) +} + +// obrimCodecDecodeSqids decodes Sqids encoded data using deterministic length validation. +func obrimCodecDecodeSqids(configuration *obrimCodecConfiguration) (string, error) { + sqidsInstance, err := sqids.New(sqids.Options{ + Alphabet: configuration.Charset, + MinLength: uint8(configuration.Length), + }) + if err != nil { + return "", err + } + + numbers, err := sqidsInstance.Decode(obrimCodecNormalizeInput(configuration.Data)) + if err != nil { + return "", err + } + + output := make([]byte, 0) + + for _, value := range numbers { + output = append(output, byte(value)) + } + + return obrimCodecReverseTransform(string(output), configuration.Charset, configuration.Salt), nil +} diff --git a/essential/visible/service/helper/datetime/datetime.go b/essential/visible/service/helper/datetime/datetime.go index e69de29..2300f1d 100644 --- a/essential/visible/service/helper/datetime/datetime.go +++ b/essential/visible/service/helper/datetime/datetime.go @@ -0,0 +1,218 @@ +package datetime + +import ( + "encoding/binary" + "fmt" + "log" + "net" + "strconv" + "time" +) + +const ( + obrimDatetimeTypeLocal = "local" + obrimDatetimeTypeTrusted = "trusted" + + obrimDatetimeCodeSuccess = "SUCCESS_DATETIME_RETRIEVED" + obrimDatetimeCodeInvalidType = "FAILED_INVALID_TYPE" + obrimDatetimeCodeInvalidFormat = "FAILED_INVALID_FORMAT" + obrimDatetimeCodeTrustedTime = "FAILED_TRUSTED_TIME_RETRIEVAL" + obrimDatetimeCodeInternal = "FAILED_INTERNAL" + obrimDatetimeTrustedNTPPoolServer = "pool.ntp.org:123" +) + +// ObrimDatetimePayload represents datetime payload data. +type ObrimDatetimePayload struct { + Value string `json:"value"` +} + +// ObrimDatetimeResponse represents the standard utility response. +type ObrimDatetimeResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload *ObrimDatetimePayload `json:"payload"` +} + +// obrimDatetimePatternMap stores supported format mappings. +var obrimDatetimePatternMap = map[string]string{ + "yyyy-MM-dd": "2006-01-02", + "MMM dd, yyyy": "Jan 02, 2006", + "MMMM dd, yyyy": "January 02, 2006", + "EEE, MMM dd, yyyy": "Mon, Jan 02, 2006", + "EEE, MMMM dd, yyyy": "Mon, January 02, 2006", + "EEEE, MMM dd, yyyy": "Monday, Jan 02, 2006", + "EEEE, MMMM dd, yyyy": "Monday, January 02, 2006", + "HH:mm:ss": "15:04:05", + "hh:mm:ss a": "03:04:05 PM", + "yyyy-MM-dd HH:mm:ss": "2006-01-02 15:04:05", + "yyyy-MM-dd HH:mm:ss z": "2006-01-02 15:04:05 MST", + "yyyy-MM-dd'T'HH:mm:ssXXX": time.RFC3339, +} + +// ObrimDatetime retrieves and formats current datetime from a selected source. +func ObrimDatetime(datetimeType string, config map[string]string) ObrimDatetimeResponse { + log.Println("[datetime] initialization started") + + if code := obrimDatetimeValidate(datetimeType, config); code != "" { + log.Printf("[datetime] validation failed: %s", code) + return obrimDatetimeResponse(false, code, "") + } + + format := config["format"] + + var ( + currentTime time.Time + err error + ) + + switch datetimeType { + case obrimDatetimeTypeLocal: + currentTime = obrimDatetimeLocal() + + case obrimDatetimeTypeTrusted: + currentTime, err = obrimDatetimeTrusted() + if err != nil { + log.Printf("[datetime] trusted retrieval failed: %v", err) + return obrimDatetimeResponse(false, obrimDatetimeCodeTrustedTime, "") + } + + default: + return obrimDatetimeResponse(false, obrimDatetimeCodeInvalidType, "") + } + + value := obrimDatetimeFormatApply(currentTime, format) + + log.Println("[datetime] retrieval completed successfully") + + return obrimDatetimeResponse(true, obrimDatetimeCodeSuccess, value) +} + +// obrimDatetimeValidate validates utility inputs. +func obrimDatetimeValidate(datetimeType string, config map[string]string) string { + switch datetimeType { + case obrimDatetimeTypeLocal, obrimDatetimeTypeTrusted: + default: + return obrimDatetimeCodeInvalidType + } + + format, exists := config["format"] + if !exists || format == "" { + return obrimDatetimeCodeInvalidFormat + } + + if !obrimDatetimePatternValidate(format) { + return obrimDatetimeCodeInvalidFormat + } + + return "" +} + +// obrimDatetimePatternValidate validates supported format patterns. +func obrimDatetimePatternValidate(pattern string) bool { + _, exists := obrimDatetimePatternMap[pattern] + + if exists { + return true + } + + switch pattern { + case "timestampsec", "timestampmil": + return true + } + + return false +} + +// obrimDatetimePatternResolve resolves patterns into layouts. +func obrimDatetimePatternResolve(pattern string) (string, bool) { + layout, exists := obrimDatetimePatternMap[pattern] + return layout, exists +} + +// obrimDatetimeFormatApply formats datetime values. +func obrimDatetimeFormatApply(datetimeValue time.Time, pattern string) string { + switch pattern { + case "timestampsec": + return strconv.FormatInt(datetimeValue.Unix(), 10) + + case "timestampmil": + return strconv.FormatInt(datetimeValue.UnixMilli(), 10) + } + + layout, exists := obrimDatetimePatternResolve(pattern) + if !exists { + return "" + } + + return datetimeValue.Format(layout) +} + +// obrimDatetimeResponse builds standardized responses. +func obrimDatetimeResponse(status bool, code string, value string) ObrimDatetimeResponse { + if !status { + return ObrimDatetimeResponse{ + Status: false, + Code: code, + Payload: nil, + } + } + + return ObrimDatetimeResponse{ + Status: true, + Code: code, + Payload: &ObrimDatetimePayload{ + Value: value, + }, + } +} + +// obrimDatetimeLocal retrieves local system time. +func obrimDatetimeLocal() time.Time { + return time.Now() +} + +// obrimDatetimeTrusted retrieves trusted NTP time. +func obrimDatetimeTrusted() (time.Time, error) { + return obrimDatetimeTrustedSync() +} + +// obrimDatetimeTrustedSync synchronizes with framework-managed NTP source. +func obrimDatetimeTrustedSync() (time.Time, error) { + connection, err := net.DialTimeout("udp", obrimDatetimeTrustedNTPPoolServer, 5*time.Second) + if err != nil { + return time.Time{}, err + } + + defer connection.Close() + + request := make([]byte, 48) + request[0] = 0x1B + + if _, err = connection.Write(request); err != nil { + return time.Time{}, err + } + + if err = connection.SetDeadline(time.Now().Add(5 * time.Second)); err != nil { + return time.Time{}, err + } + + response := make([]byte, 48) + + if _, err = connection.Read(response); err != nil { + return time.Time{}, err + } + + seconds := binary.BigEndian.Uint32(response[40:44]) + fraction := binary.BigEndian.Uint32(response[44:48]) + + const ntpEpochOffset = 2208988800 + + unixSeconds := int64(seconds) - ntpEpochOffset + nanoseconds := (int64(fraction) * 1e9) >> 32 + + if unixSeconds < 0 { + return time.Time{}, fmt.Errorf("invalid ntp timestamp") + } + + return time.Unix(unixSeconds, nanoseconds).UTC(), nil +} diff --git a/essential/visible/service/helper/filesystem/filesystem.go b/essential/visible/service/helper/filesystem/filesystem.go index e69de29..619775c 100644 --- a/essential/visible/service/helper/filesystem/filesystem.go +++ b/essential/visible/service/helper/filesystem/filesystem.go @@ -0,0 +1,531 @@ +package filesystem + +import ( + "errors" + "fmt" + "io/fs" + "os" + "os/user" + "path/filepath" + "runtime" + "sort" + "strings" + "time" +) + +// ObrimFilesystemResult represents the standardized utility response. +type ObrimFilesystemResult struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload any `json:"payload"` +} + +// obrimFilesystemEntry represents a filesystem listing entry. +type obrimFilesystemEntry struct { + Name string `json:"name"` + Path string `json:"path"` + Entity string `json:"entity"` + Size int64 `json:"size"` + ModifiedTime time.Time `json:"modified_time"` +} + +// ObrimFilesystem executes a filesystem operation and returns a structured result. +func ObrimFilesystem(operationType string, config map[string]any) map[string]any { + if config == nil { + config = map[string]any{} + } + + if err := obrimFilesystemValidate(operationType, config); err != nil { + return ObrimStatus(false, "FAILED_VALIDATION", nil) + } + + switch operationType { + case "list": + return obrimFilesystemList(config) + + case "check": + return obrimFilesystemCheck(config) + + case "create": + return obrimFilesystemCreate(config) + + case "delete": + return obrimFilesystemDelete(config) + + case "permission": + return obrimFilesystemPermission(config) + + default: + return ObrimStatus(false, "FAILED_UNSUPPORTED_TYPE", nil) + } +} + +// obrimFilesystemValidate validates operation type and configuration. +func obrimFilesystemValidate(operationType string, config map[string]any) error { + allowedTypes := map[string]bool{ + "list": true, + "check": true, + "create": true, + "delete": true, + "permission": true, + } + + if !allowedTypes[operationType] { + return errors.New("unsupported type") + } + + switch operationType { + case "create", "delete": + entity := strings.TrimSpace(fmt.Sprintf("%v", config["entity"])) + if entity != "file" && entity != "directory" { + return errors.New("invalid entity") + } + } + + if strategy, ok := config["strategy"]; ok { + value := fmt.Sprintf("%v", strategy) + + if value != "user" && value != "custom" { + return errors.New("invalid strategy") + } + + if value == "custom" { + if strings.TrimSpace(fmt.Sprintf("%v", config["base"])) == "" { + return errors.New("missing base") + } + } + } + + return nil +} + +// obrimFilesystemResolve resolves and normalizes filesystem paths. +func obrimFilesystemResolve(config map[string]any) (string, error) { + base := "." + + if strategy, ok := config["strategy"]; ok { + switch fmt.Sprintf("%v", strategy) { + case "user": + currentUser, err := user.Current() + if err != nil { + return "", err + } + + base = currentUser.HomeDir + + case "custom": + base = fmt.Sprintf("%v", config["base"]) + } + } + + name := strings.TrimSpace(fmt.Sprintf("%v", config["name"])) + + resolved := filepath.Join(base, name) + + absolute, err := filepath.Abs(filepath.Clean(resolved)) + if err != nil { + return "", err + } + + return absolute, nil +} + +// obrimFilesystemPayload builds standardized payload structures. +func obrimFilesystemPayload(payload map[string]any) map[string]any { + return payload +} + +// obrimFilesystemEntityType determines filesystem entity type. +func obrimFilesystemEntityType(path string) string { + info, err := os.Stat(path) + if err != nil { + return "unknown" + } + + if info.IsDir() { + return "directory" + } + + return "file" +} + +// obrimFilesystemPermissionValidate validates permission specifications. +func obrimFilesystemPermissionValidate(permission string) error { + if permission == "" { + return errors.New("permission required") + } + + _, err := fs.FileMode(0).String(), error(nil) + return err +} + +// obrimFilesystemList executes filesystem listing operations. +func obrimFilesystemList(config map[string]any) map[string]any { + basePath, err := obrimFilesystemResolve(config) + if err != nil { + return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil) + } + + entries, err := obrimFilesystemListTraverse(basePath, config) + if err != nil { + return ObrimStatus(false, "FAILED_EXECUTION", nil) + } + + entries = obrimFilesystemListFilter(entries, config) + entries = obrimFilesystemListSort(entries, config) + + payload := obrimFilesystemPayload(map[string]any{ + "entries": entries, + "total": len(entries), + "recursive": getBool(config, "recursive"), + "base_path": basePath, + }) + + return ObrimStatus(true, "SUCCESS_LIST", payload) +} + +// obrimFilesystemListTraverse traverses filesystem entities according to listing configuration. +func obrimFilesystemListTraverse(basePath string, config map[string]any) ([]obrimFilesystemEntry, error) { + var entries []obrimFilesystemEntry + + recursive := getBool(config, "recursive") + + if recursive { + err := filepath.Walk(basePath, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + + if path == basePath { + return nil + } + + entries = append(entries, obrimFilesystemEntry{ + Name: info.Name(), + Path: path, + Entity: obrimFilesystemEntityType(path), + Size: info.Size(), + ModifiedTime: info.ModTime(), + }) + + return nil + }) + + return entries, err + } + + list, err := os.ReadDir(basePath) + if err != nil { + return nil, err + } + + for _, item := range list { + info, _ := item.Info() + + entries = append(entries, obrimFilesystemEntry{ + Name: item.Name(), + Path: filepath.Join(basePath, item.Name()), + Entity: map[bool]string{true: "directory", false: "file"}[item.IsDir()], + Size: info.Size(), + ModifiedTime: info.ModTime(), + }) + } + + return entries, nil +} + +// obrimFilesystemListFilter applies entity and pattern filtering. +func obrimFilesystemListFilter(entries []obrimFilesystemEntry, config map[string]any) []obrimFilesystemEntry { + var filtered []obrimFilesystemEntry + + entity := strings.TrimSpace(fmt.Sprintf("%v", config["entity"])) + pattern := strings.TrimSpace(fmt.Sprintf("%v", config["filter_pattern"])) + includeHidden := getBool(config, "include_hidden") + + for _, entry := range entries { + if !includeHidden && strings.HasPrefix(entry.Name, ".") { + continue + } + + if entity != "" && entry.Entity != entity { + continue + } + + if pattern != "" { + match, _ := filepath.Match(pattern, entry.Name) + if !match { + continue + } + } + + filtered = append(filtered, entry) + } + + return filtered +} + +// obrimFilesystemListSort applies result sorting behavior. +func obrimFilesystemListSort(entries []obrimFilesystemEntry, config map[string]any) []obrimFilesystemEntry { + sortBy := strings.TrimSpace(fmt.Sprintf("%v", config["sort_by"])) + order := strings.TrimSpace(fmt.Sprintf("%v", config["sort_order"])) + + sort.Slice(entries, func(i, j int) bool { + var result bool + + switch sortBy { + case "size": + result = entries[i].Size < entries[j].Size + + case "modified": + result = entries[i].ModifiedTime.Before(entries[j].ModifiedTime) + + default: + result = entries[i].Name < entries[j].Name + } + + if order == "desc" { + return !result + } + + return result + }) + + return entries +} + +// obrimFilesystemCheck executes filesystem validation operations. +func obrimFilesystemCheck(config map[string]any) map[string]any { + path, err := obrimFilesystemResolve(config) + if err != nil { + return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil) + } + + _, err = os.Stat(path) + + exists := err == nil + + readable, writable := obrimFilesystemCheckAccess(path) + + payload := obrimFilesystemPayload(map[string]any{ + "path": path, + "exists": exists, + "entity": obrimFilesystemEntityType(path), + "readable": readable, + "writable": writable, + }) + + return ObrimStatus(true, "SUCCESS_CHECK", payload) +} + +// obrimFilesystemCheckAccess verifies filesystem accessibility requirements. +func obrimFilesystemCheckAccess(path string) (bool, bool) { + readable := true + writable := true + + file, err := os.Open(path) + if err != nil { + readable = false + } else { + _ = file.Close() + } + + file, err = os.OpenFile(path, os.O_WRONLY, 0) + if err != nil { + writable = false + } else { + _ = file.Close() + } + + return readable, writable +} + +// obrimFilesystemCreate executes filesystem entity creation operations. +func obrimFilesystemCreate(config map[string]any) map[string]any { + path, err := obrimFilesystemResolve(config) + if err != nil { + return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil) + } + + entity := fmt.Sprintf("%v", config["entity"]) + + if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil { + return ObrimStatus(false, "FAILED_EXECUTION", nil) + } + + switch entity { + case "file": + err = obrimFilesystemCreateFile(path) + + case "directory": + err = obrimFilesystemCreateDirectory(path) + } + + if err != nil { + return ObrimStatus(false, "FAILED_EXECUTION", nil) + } + + hidden := getBool(config, "hidden") + + payload := obrimFilesystemPayload(map[string]any{ + "path": path, + "entity": entity, + "hidden": hidden, + "created": true, + }) + + return ObrimStatus(true, "SUCCESS_CREATE", payload) +} + +// obrimFilesystemCreateFile creates a filesystem file. +func obrimFilesystemCreateFile(path string) error { + file, err := os.Create(path) + if err != nil { + return err + } + + return file.Close() +} + +// obrimFilesystemCreateDirectory creates a filesystem directory. +func obrimFilesystemCreateDirectory(path string) error { + return os.MkdirAll(path, 0755) +} + +// obrimFilesystemDelete executes filesystem entity deletion operations. +func obrimFilesystemDelete(config map[string]any) map[string]any { + path, err := obrimFilesystemResolve(config) + if err != nil { + return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil) + } + + entity := fmt.Sprintf("%v", config["entity"]) + + switch entity { + case "file": + err = obrimFilesystemDeleteFile(path) + + case "directory": + err = obrimFilesystemDeleteDirectory(path, getBool(config, "recursive")) + } + + if err != nil { + return ObrimStatus(false, "FAILED_EXECUTION", nil) + } + + payload := obrimFilesystemPayload(map[string]any{ + "path": path, + "entity": entity, + "deleted": true, + }) + + return ObrimStatus(true, "SUCCESS_DELETE", payload) +} + +// obrimFilesystemDeleteFile deletes a filesystem file. +func obrimFilesystemDeleteFile(path string) error { + return os.Remove(path) +} + +// obrimFilesystemDeleteDirectory deletes a filesystem directory. +func obrimFilesystemDeleteDirectory(path string, recursive bool) error { + if recursive { + return os.RemoveAll(path) + } + + return os.Remove(path) +} + +// obrimFilesystemPermission executes filesystem permission operations. +func obrimFilesystemPermission(config map[string]any) map[string]any { + mode := strings.TrimSpace(fmt.Sprintf("%v", config["mode"])) + + switch mode { + case "write": + return obrimFilesystemPermissionWrite(config) + + default: + return obrimFilesystemPermissionRead(config) + } +} + +// obrimFilesystemPermissionRead retrieves filesystem permissions. +func obrimFilesystemPermissionRead(config map[string]any) map[string]any { + path, err := obrimFilesystemResolve(config) + if err != nil { + return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil) + } + + info, err := os.Stat(path) + if err != nil { + return ObrimStatus(false, "FAILED_EXECUTION", nil) + } + + payload := obrimFilesystemPayload(map[string]any{ + "path": path, + "permission": info.Mode().Perm().String(), + "mode": "read", + "updated": false, + }) + + return ObrimStatus(true, "SUCCESS_PERMISSION", payload) +} + +// obrimFilesystemPermissionWrite updates filesystem permissions. +func obrimFilesystemPermissionWrite(config map[string]any) map[string]any { + path, err := obrimFilesystemResolve(config) + if err != nil { + return ObrimStatus(false, "FAILED_PATH_RESOLUTION", nil) + } + + permission := strings.TrimSpace(fmt.Sprintf("%v", config["permission"])) + + if err := obrimFilesystemPermissionValidate(permission); err != nil { + return ObrimStatus(false, "FAILED_VALIDATION", nil) + } + + var mode os.FileMode + + _, err = fmt.Sscanf(permission, "%o", &mode) + if err != nil { + return ObrimStatus(false, "FAILED_VALIDATION", nil) + } + + if runtime.GOOS != "windows" { + if err := os.Chmod(path, mode); err != nil { + return ObrimStatus(false, "FAILED_EXECUTION", nil) + } + } + + payload := obrimFilesystemPayload(map[string]any{ + "path": path, + "permission": permission, + "mode": "write", + "updated": true, + }) + + return ObrimStatus(true, "SUCCESS_PERMISSION", payload) +} + +// ObrimStatus returns standardized utility status responses. +func ObrimStatus(status bool, code string, payload any) map[string]any { + return map[string]any{ + "status": status, + "code": code, + "payload": payload, + } +} + +// getBool safely reads boolean config values. +func getBool(config map[string]any, key string) bool { + value, ok := config[key] + if !ok { + return false + } + + boolean, ok := value.(bool) + if !ok { + return false + } + + return boolean +} diff --git a/essential/visible/service/helper/hash/hash.go b/essential/visible/service/helper/hash/hash.go index e69de29..747c771 100644 --- a/essential/visible/service/helper/hash/hash.go +++ b/essential/visible/service/helper/hash/hash.go @@ -0,0 +1,412 @@ +package hash + +import ( + "crypto/rand" + "crypto/sha256" + "crypto/sha512" + "encoding/hex" +) + +// ObrimHashResponse represents the standard utility response structure. +type ObrimHashResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload interface{} `json:"payload"` +} + +// ObrimHashCalculateStandardPayload represents a standard hash generation payload. +type ObrimHashCalculateStandardPayload struct { + Algorithm string `json:"algorithm"` + Hash string `json:"hash"` +} + +// ObrimHashCalculateSaltedPayload represents a salted hash generation payload. +type ObrimHashCalculateSaltedPayload struct { + Algorithm string `json:"algorithm"` + Hash string `json:"hash"` + Salt string `json:"salt"` +} + +// ObrimHashCompareStandardPayload represents a standard hash comparison payload. +type ObrimHashCompareStandardPayload struct { + Algorithm string `json:"algorithm"` + Matched bool `json:"matched"` + Hash string `json:"hash"` +} + +// ObrimHashCompareSaltedPayload represents a salted hash comparison payload. +type ObrimHashCompareSaltedPayload struct { + Algorithm string `json:"algorithm"` + Matched bool `json:"matched"` + Hash string `json:"hash"` + Salt string `json:"salt"` +} + +// ObrimHashConfig represents utility configuration. +type ObrimHashConfig struct { + Mode string + Algorithm string + Value string + Hash string + Salt string +} + +// obrimHashAllowedConfigKeys defines supported configuration keys. +var obrimHashAllowedConfigKeys = map[string]struct{}{ + "mode": {}, + "algorithm": {}, + "value": {}, + "hash": {}, + "salt": {}, +} + +// ObrimHash processes hash generation and verification requests. +func ObrimHash(hashType string, config map[string]string) ObrimHashResponse { + if response := obrimHashValidateType(hashType); response != nil { + return *response + } + + if response := obrimHashValidateConfig(hashType, config); response != nil { + return *response + } + + parsedConfig := ObrimHashConfig{ + Mode: config["mode"], + Algorithm: config["algorithm"], + Value: config["value"], + Hash: config["hash"], + Salt: config["salt"], + } + + if response := obrimHashValidateMode(parsedConfig.Mode); response != nil { + return *response + } + + if response := obrimHashValidateAlgorithm(parsedConfig.Mode, parsedConfig.Algorithm); response != nil { + return *response + } + + switch hashType { + case "calculate": + return obrimHashCalculate(parsedConfig) + case "compare": + return obrimHashCompare(parsedConfig) + default: + return obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_TYPE") + } +} + +// obrimHashValidateType validates operation type. +func obrimHashValidateType(hashType string) *ObrimHashResponse { + switch hashType { + case "calculate", "compare": + return nil + default: + response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_TYPE") + return &response + } +} + +// obrimHashValidateMode validates hashing mode. +func obrimHashValidateMode(mode string) *ObrimHashResponse { + switch mode { + case "standard", "salted": + return nil + default: + response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_MODE") + return &response + } +} + +// obrimHashValidateAlgorithm validates hashing algorithm selection. +func obrimHashValidateAlgorithm(mode string, algorithm string) *ObrimHashResponse { + switch mode { + case "standard": + switch algorithm { + case "sha256", "sha512": + return nil + } + case "salted": + switch algorithm { + case "salted_sha256", "salted_sha512": + return nil + } + } + + response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_ALGORITHM") + return &response +} + +// obrimHashValidateConfig validates configuration keys and values. +func obrimHashValidateConfig(hashType string, config map[string]string) *ObrimHashResponse { + if config == nil { + response := obrimHashBuildErrorPayload("FAILED_INVALID_CONFIG") + return &response + } + + for key := range config { + if _, exists := obrimHashAllowedConfigKeys[key]; !exists { + response := obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_CONFIG_KEY") + return &response + } + } + + if config["mode"] == "" { + response := obrimHashBuildErrorPayload("FAILED_MISSING_MODE") + return &response + } + + if config["algorithm"] == "" { + response := obrimHashBuildErrorPayload("FAILED_MISSING_ALGORITHM") + return &response + } + + if config["value"] == "" { + response := obrimHashBuildErrorPayload("FAILED_EMPTY_VALUE") + return &response + } + + switch hashType { + case "calculate": + if config["mode"] == "standard" && config["salt"] != "" { + response := obrimHashBuildErrorPayload("FAILED_SALT_NOT_ALLOWED") + return &response + } + + case "compare": + if config["hash"] == "" { + response := obrimHashBuildErrorPayload("FAILED_EMPTY_HASH") + return &response + } + + if config["mode"] == "standard" && config["salt"] != "" { + response := obrimHashBuildErrorPayload("FAILED_SALT_NOT_ALLOWED") + return &response + } + + if config["mode"] == "salted" && config["salt"] == "" { + response := obrimHashBuildErrorPayload("FAILED_MISSING_SALT") + return &response + } + } + + return nil +} + +// obrimHashGenerateHash executes the selected hashing algorithm. +func obrimHashGenerateHash(algorithm string, value string, salt string) (string, bool) { + var input []byte + + switch algorithm { + case "sha256": + input = []byte(value) + + // Generate SHA-256 hash. + sum := sha256.Sum256(input) + return hex.EncodeToString(sum[:]), true + + case "sha512": + input = []byte(value) + + // Generate SHA-512 hash. + sum := sha512.Sum512(input) + return hex.EncodeToString(sum[:]), true + + case "salted_sha256": + input = []byte(value + salt) + + // Generate salted SHA-256 hash. + sum := sha256.Sum256(input) + return hex.EncodeToString(sum[:]), true + + case "salted_sha512": + input = []byte(value + salt) + + // Generate salted SHA-512 hash. + sum := sha512.Sum512(input) + return hex.EncodeToString(sum[:]), true + } + + return "", false +} + +// obrimHashCalculate processes hash generation requests. +func obrimHashCalculate(config ObrimHashConfig) ObrimHashResponse { + switch config.Mode { + case "standard": + return obrimHashCalculateStandard(config) + + case "salted": + return obrimHashCalculateSalted(config) + + default: + return obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_MODE") + } +} + +// obrimHashCalculateStandard generates a deterministic hash. +func obrimHashCalculateStandard(config ObrimHashConfig) ObrimHashResponse { + hashValue, success := obrimHashGenerateHash( + config.Algorithm, + config.Value, + "", + ) + + if !success { + return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION") + } + + payload := ObrimHashCalculateStandardPayload{ + Algorithm: config.Algorithm, + Hash: hashValue, + } + + return obrimHashBuildSuccessPayload( + "SUCCESS_HASH_GENERATED", + payload, + ) +} + +// obrimHashCalculateSalted generates a salted hash and salt pair. +func obrimHashCalculateSalted(config ObrimHashConfig) ObrimHashResponse { + // Store active salt value. + activeSalt := config.Salt + + if activeSalt == "" { + generatedSalt, success := obrimHashGenerateSalt() + if !success { + return obrimHashBuildErrorPayload("FAILED_SALT_GENERATION") + } + + activeSalt = generatedSalt + } + + hashValue, success := obrimHashGenerateHash( + config.Algorithm, + config.Value, + activeSalt, + ) + + if !success { + return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION") + } + + payload := ObrimHashCalculateSaltedPayload{ + Algorithm: config.Algorithm, + Hash: hashValue, + Salt: activeSalt, + } + + return obrimHashBuildSuccessPayload( + "SUCCESS_HASH_GENERATED", + payload, + ) +} + +// obrimHashGenerateSalt generates a cryptographically secure salt. +func obrimHashGenerateSalt() (string, bool) { + // Allocate random salt bytes. + saltBytes := make([]byte, 32) + + if _, err := rand.Read(saltBytes); err != nil { + return "", false + } + + return hex.EncodeToString(saltBytes), true +} + +// obrimHashCompare processes hash verification requests. +func obrimHashCompare(config ObrimHashConfig) ObrimHashResponse { + switch config.Mode { + case "standard": + return obrimHashCompareStandard(config) + + case "salted": + return obrimHashCompareSalted(config) + + default: + return obrimHashBuildErrorPayload("FAILED_UNSUPPORTED_MODE") + } +} + +// obrimHashCompareStandard regenerates a standard hash for verification. +func obrimHashCompareStandard(config ObrimHashConfig) ObrimHashResponse { + regeneratedHash, success := obrimHashGenerateHash( + config.Algorithm, + config.Value, + "", + ) + + if !success { + return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION") + } + + matched := obrimHashVerify( + regeneratedHash, + config.Hash, + ) + + payload := ObrimHashCompareStandardPayload{ + Algorithm: config.Algorithm, + Matched: matched, + Hash: config.Hash, + } + + return obrimHashBuildSuccessPayload( + "SUCCESS_HASH_COMPARED", + payload, + ) +} + +// obrimHashCompareSalted regenerates a salted hash for verification. +func obrimHashCompareSalted(config ObrimHashConfig) ObrimHashResponse { + regeneratedHash, success := obrimHashGenerateHash( + config.Algorithm, + config.Value, + config.Salt, + ) + + if !success { + return obrimHashBuildErrorPayload("FAILED_HASH_GENERATION") + } + + matched := obrimHashVerify( + regeneratedHash, + config.Hash, + ) + + payload := ObrimHashCompareSaltedPayload{ + Algorithm: config.Algorithm, + Matched: matched, + Hash: config.Hash, + Salt: config.Salt, + } + + return obrimHashBuildSuccessPayload( + "SUCCESS_HASH_COMPARED", + payload, + ) +} + +// obrimHashVerify compares regenerated and supplied hash values. +func obrimHashVerify(regeneratedHash string, suppliedHash string) bool { + return regeneratedHash == suppliedHash +} + +// obrimHashBuildSuccessPayload constructs success response payloads. +func obrimHashBuildSuccessPayload(code string, payload interface{}) ObrimHashResponse { + return ObrimHashResponse{ + Status: true, + Code: code, + Payload: payload, + } +} + +// obrimHashBuildErrorPayload constructs error response payloads. +func obrimHashBuildErrorPayload(code string) ObrimHashResponse { + return ObrimHashResponse{ + Status: false, + Code: code, + Payload: nil, + } +} diff --git a/essential/visible/service/helper/key/key.go b/essential/visible/service/helper/key/key.go index e69de29..d7d74c7 100644 --- a/essential/visible/service/helper/key/key.go +++ b/essential/visible/service/helper/key/key.go @@ -0,0 +1,276 @@ +package key + +import ( + "crypto/aes" + "crypto/ed25519" + "crypto/rand" + "encoding/base64" + "strings" + "time" +) + +// ObrimKeyResponse represents the standardized utility response. +type ObrimKeyResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload map[string]any `json:"payload"` +} + +// obrimKeyAllowedSymmetricFields defines allowed symmetric configuration fields. +var obrimKeyAllowedSymmetricFields = map[string]struct{}{ + "algorithm": {}, + "key_size": {}, +} + +// obrimKeyAllowedAsymmetricFields defines allowed asymmetric configuration fields. +var obrimKeyAllowedAsymmetricFields = map[string]struct{}{ + "algorithm": {}, +} + +// ObrimKey dispatches key generation requests and orchestrates validation, generation, and response construction. +func ObrimKey(keyType string, config map[string]any) map[string]any { + if response := obrimKeyValidate(keyType, config); response != nil { + return response + } + + return obrimKeyDispatch(keyType, config) +} + +// obrimKeyValidate validates common request structure and schema integrity. +func obrimKeyValidate(keyType string, config map[string]any) map[string]any { + if strings.TrimSpace(keyType) == "" { + return obrimKeyBuildError("FAILED_MISSING_TYPE") + } + + if config == nil { + return obrimKeyBuildError("FAILED_MISSING_CONFIG") + } + + normalizedType := strings.ToLower(strings.TrimSpace(keyType)) + + switch normalizedType { + case "symmetric": + return obrimKeyValidateSymmetricConfig(config) + + case "asymmetric": + return obrimKeyValidateAsymmetricConfig(config) + + default: + return obrimKeyBuildError("FAILED_UNSUPPORTED_TYPE") + } +} + +// obrimKeyDispatch routes execution to the appropriate cryptographic workflow. +func obrimKeyDispatch(keyType string, config map[string]any) map[string]any { + switch strings.ToLower(strings.TrimSpace(keyType)) { + case "symmetric": + return obrimKeyGenerateSymmetric(config) + + case "asymmetric": + return obrimKeyGenerateAsymmetric(config) + + default: + return obrimKeyBuildError("FAILED_UNSUPPORTED_TYPE") + } +} + +// obrimKeyValidateSymmetricConfig validates symmetric configuration values. +func obrimKeyValidateSymmetricConfig(config map[string]any) map[string]any { + for field := range config { + if _, exists := obrimKeyAllowedSymmetricFields[field]; !exists { + return obrimKeyBuildError("FAILED_INVALID_CONFIG_FIELD") + } + } + + algorithmValue, exists := config["algorithm"] + if !exists { + return obrimKeyBuildError("FAILED_MISSING_ALGORITHM") + } + + algorithm, ok := algorithmValue.(string) + if !ok { + return obrimKeyBuildError("FAILED_INVALID_ALGORITHM") + } + + if strings.ToLower(strings.TrimSpace(algorithm)) != "aes" { + return obrimKeyBuildError("FAILED_UNSUPPORTED_ALGORITHM") + } + + keySizeValue, exists := config["key_size"] + if !exists { + return obrimKeyBuildError("FAILED_MISSING_KEY_SIZE") + } + + keySize, ok := obrimKeyConvertInt(keySizeValue) + if !ok { + return obrimKeyBuildError("FAILED_INVALID_KEY_SIZE") + } + + switch keySize { + case 128, 192, 256: + return nil + default: + return obrimKeyBuildError("FAILED_UNSUPPORTED_KEY_SIZE") + } +} + +// obrimKeyValidateAsymmetricConfig validates asymmetric configuration values. +func obrimKeyValidateAsymmetricConfig(config map[string]any) map[string]any { + for field := range config { + if _, exists := obrimKeyAllowedAsymmetricFields[field]; !exists { + return obrimKeyBuildError("FAILED_INVALID_CONFIG_FIELD") + } + } + + algorithmValue, exists := config["algorithm"] + if !exists { + return obrimKeyBuildError("FAILED_MISSING_ALGORITHM") + } + + algorithm, ok := algorithmValue.(string) + if !ok { + return obrimKeyBuildError("FAILED_INVALID_ALGORITHM") + } + + if strings.ToLower(strings.TrimSpace(algorithm)) != "ecc" { + return obrimKeyBuildError("FAILED_UNSUPPORTED_ALGORITHM") + } + + return nil +} + +// obrimKeyGenerateSymmetric generates symmetric key material. +func obrimKeyGenerateSymmetric(config map[string]any) map[string]any { + normalizedConfig := obrimKeyNormalizeSymmetricConfig(config) + + payload, errorCode := obrimKeyGenerateAES(normalizedConfig) + if errorCode != "" { + return obrimKeyBuildError(errorCode) + } + + return obrimKeyBuildPayload(payload) +} + +// obrimKeyGenerateAES generates AES key material. +func obrimKeyGenerateAES(config map[string]any) (map[string]any, string) { + keySize := config["key_size"].(int) + + // obrimKeyMaterial stores generated AES key bytes. + keyMaterial := make([]byte, keySize/8) + + if _, err := rand.Read(keyMaterial); err != nil { + return nil, "FAILED_KEY_GENERATION" + } + + if _, err := aes.NewCipher(keyMaterial); err != nil { + return nil, "FAILED_KEY_GENERATION" + } + + return map[string]any{ + "type": "symmetric", + "algorithm": "aes", + "key_size": keySize, + "key_material": base64.StdEncoding.EncodeToString(keyMaterial), + "generated_at": time.Now().UTC().Format(time.RFC3339), + }, "" +} + +// obrimKeyNormalizeSymmetricConfig normalizes symmetric configuration values. +func obrimKeyNormalizeSymmetricConfig(config map[string]any) map[string]any { + keySize, _ := obrimKeyConvertInt(config["key_size"]) + + return map[string]any{ + "algorithm": strings.ToLower(strings.TrimSpace(config["algorithm"].(string))), + "key_size": keySize, + } +} + +// obrimKeyGenerateAsymmetric generates asymmetric key material. +func obrimKeyGenerateAsymmetric(config map[string]any) map[string]any { + normalizedConfig := obrimKeyNormalizeAsymmetricConfig(config) + + payload, errorCode := obrimKeyGenerateECC(normalizedConfig) + if errorCode != "" { + return obrimKeyBuildError(errorCode) + } + + return obrimKeyBuildPayload(payload) +} + +// obrimKeyGenerateECC generates ECC key pair material. +func obrimKeyGenerateECC(config map[string]any) (map[string]any, string) { + publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader) + if err != nil { + return nil, "FAILED_KEY_GENERATION" + } + + return map[string]any{ + "type": "asymmetric", + "algorithm": "ecc", + "curve": "eddsa", + "public_key": base64.StdEncoding.EncodeToString(publicKey), + "private_key": base64.StdEncoding.EncodeToString(privateKey), + "generated_at": time.Now().UTC().Format(time.RFC3339), + }, "" +} + +// obrimKeyNormalizeAsymmetricConfig normalizes asymmetric configuration values. +func obrimKeyNormalizeAsymmetricConfig(config map[string]any) map[string]any { + return map[string]any{ + "algorithm": strings.ToLower(strings.TrimSpace(config["algorithm"].(string))), + } +} + +// obrimKeyBuildPayload constructs standardized success payloads. +func obrimKeyBuildPayload(payload map[string]any) map[string]any { + return map[string]any{ + "status": true, + "code": "SUCCESS_KEY_GENERATED", + "payload": payload, + } +} + +// obrimKeyBuildError constructs standardized error responses. +func obrimKeyBuildError(code string) map[string]any { + return map[string]any{ + "status": false, + "code": code, + "payload": nil, + } +} + +// obrimKeyConvertInt converts supported numeric values to int. +func obrimKeyConvertInt(value any) (int, bool) { + switch converted := value.(type) { + case int: + return converted, true + case int8: + return int(converted), true + case int16: + return int(converted), true + case int32: + return int(converted), true + case int64: + return int(converted), true + case uint: + return int(converted), true + case uint8: + return int(converted), true + case uint16: + return int(converted), true + case uint32: + return int(converted), true + case uint64: + return int(converted), true + case float32: + if float32(int(converted)) == converted { + return int(converted), true + } + case float64: + if float64(int(converted)) == converted { + return int(converted), true + } + } + + return 0, false +} diff --git a/essential/visible/service/helper/log/log.go b/essential/visible/service/helper/log/log.go index e69de29..d786706 100644 --- a/essential/visible/service/helper/log/log.go +++ b/essential/visible/service/helper/log/log.go @@ -0,0 +1,183 @@ +package log + +import ( + "fmt" + "os" + "path/filepath" + "runtime" + "strings" + "time" + + "go/module/essential/visible/service/helper/retriever" +) + +const ( + obrimLogMetadataResource = "software/metadata" + obrimLogMetadataPath = "software.lower" + obrimLogFileExtension = ".log" +) + +// ObrimLog writes a structured log entry. +func ObrimLog(label string, message string) { + softwareName := obrimLogSoftwareNameResolve() + if softwareName == "" { + return + } + + timestamp := obrimLogTimestampGenerate() + entry := obrimLogEntryBuild(timestamp, label, message) + + directoryPath := obrimLogDirectoryResolve(softwareName) + if directoryPath == "" { + return + } + + filePath := obrimLogFileResolve(directoryPath, softwareName) + + if err := obrimLogFileEnsure(directoryPath, filePath); err != nil { + return + } + + _ = obrimLogWrite(filePath, entry) +} + +// obrimLogTimestampGenerate generates deterministic timestamps. +func obrimLogTimestampGenerate() string { + return time.Now().UTC().Format(time.RFC3339) +} + +// obrimLogEntryBuild constructs formatted log entries. +func obrimLogEntryBuild(timestamp string, label string, message string) string { + return fmt.Sprintf("[%s] [%s] %s\n", timestamp, strings.TrimSpace(label), strings.TrimSpace(message)) +} + +// obrimLogDirectoryResolve resolves platform-specific log directory paths. +func obrimLogDirectoryResolve(softwareName string) string { + switch runtime.GOOS { + case "linux": + return obrimLogDirectoryResolveLinux(softwareName) + + case "windows": + return obrimLogDirectoryResolveWindows(softwareName) + + case "darwin": + return obrimLogDirectoryResolveMacOS(softwareName) + + default: + return "" + } +} + +// obrimLogFileResolve resolves platform-specific log file paths. +func obrimLogFileResolve(directoryPath string, softwareName string) string { + return filepath.Join(directoryPath, softwareName+obrimLogFileExtension) +} + +// obrimLogFileEnsure creates and verifies required log directories and files. +func obrimLogFileEnsure(directoryPath string, filePath string) error { + if err := os.MkdirAll(directoryPath, 0o755); err != nil { + return err + } + + file, err := os.OpenFile(filePath, os.O_CREATE, 0o644) + if err != nil { + return err + } + + return file.Close() +} + +// obrimLogWrite persists log entries to the filesystem. +func obrimLogWrite(filePath string, entry string) error { + file, err := os.OpenFile( + filePath, + os.O_APPEND|os.O_CREATE|os.O_WRONLY, + 0o644, + ) + if err != nil { + return err + } + + defer file.Close() + + _, err = file.WriteString(entry) + + return err +} + +// obrimLogSoftwareNameResolve retrieves the software name from metadata resources. +func obrimLogSoftwareNameResolve() string { + value := retriever.ObrimRetriever( + "json", + map[string]any{ + "resource": obrimLogMetadataResource, + "path": obrimLogMetadataPath, + }, + ) + + switch softwareName := value.(type) { + case string: + return strings.TrimSpace(softwareName) + + default: + return "" + } +} + +// obrimLogDirectoryResolveLinux resolves Linux log directories. +func obrimLogDirectoryResolveLinux(softwareName string) string { + // Mutable XDG state directory environment value. + xdgStateHome := strings.TrimSpace(os.Getenv("XDG_STATE_HOME")) + + if xdgStateHome != "" { + return filepath.Join( + xdgStateHome, + "."+softwareName, + "logs", + ) + } + + homeDirectory, err := os.UserHomeDir() + if err != nil { + return "" + } + + return filepath.Join( + homeDirectory, + ".local", + "state", + "."+softwareName, + "logs", + ) +} + +// obrimLogDirectoryResolveWindows resolves Windows log directories. +func obrimLogDirectoryResolveWindows(softwareName string) string { + // Mutable local application data directory environment value. + localAppData := strings.TrimSpace(os.Getenv("LOCALAPPDATA")) + + if localAppData == "" { + return "" + } + + return filepath.Join( + localAppData, + softwareName, + "Logs", + ) +} + +// obrimLogDirectoryResolveMacOS resolves macOS log directories. +func obrimLogDirectoryResolveMacOS(softwareName string) string { + homeDirectory, err := os.UserHomeDir() + if err != nil { + return "" + } + + return filepath.Join( + homeDirectory, + "Library", + "Logs", + "."+softwareName, + ) +} diff --git a/essential/visible/service/helper/marker/marker.go b/essential/visible/service/helper/marker/marker.go index e69de29..b9b6c25 100644 --- a/essential/visible/service/helper/marker/marker.go +++ b/essential/visible/service/helper/marker/marker.go @@ -0,0 +1,365 @@ +package marker + +import ( + "crypto/rand" + "crypto/sha256" + "encoding/binary" + "fmt" + "math" + "strings" + "sync/atomic" + "time" +) + +// Purpose: Supported marker generation types. +const ( + obrimMarkerTypeTime = "time" + obrimMarkerTypeRandom = "random" + obrimMarkerTypeEncoded = "encoded" +) + +// Purpose: Default random marker length. +const obrimMarkerDefaultRandomLength = 16 + +// Purpose: Default encoded marker length. +const obrimMarkerDefaultEncodedLength = 16 + +// Purpose: Default character set used for marker generation. +const obrimMarkerDefaultCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789" + +// Purpose: Sequence counter used to improve uniqueness for time markers. +var obrimMarkerSequence uint64 + +// Purpose: Standardized utility response structure. +type obrimMarkerResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload any `json:"payload"` +} + +// Purpose: Time marker payload structure. +type obrimMarkerTimePayload struct { + Marker string `json:"marker"` + Epoch int64 `json:"epoch"` + Instance string `json:"instance"` +} + +// Purpose: Random marker payload structure. +type obrimMarkerRandomPayload struct { + Marker string `json:"marker"` + Length int `json:"length"` + Charset string `json:"charset"` +} + +// Purpose: Encoded marker payload structure. +type obrimMarkerEncodedPayload struct { + Marker string `json:"marker"` + Source string `json:"source"` + Length int `json:"length"` +} + +// Purpose: Generate markers using the selected marker strategy. +func ObrimMarker(markerType string, config map[string]any) map[string]any { + obrimMarkerLog("initialization", markerType) + + if config == nil { + config = map[string]any{} + } + + if err := obrimMarkerValidateType(markerType); err != nil { + obrimMarkerLog("validation_failure", err.Error()) + return obrimMarkerBuildResponse(false, "FAILED_INVALID_TYPE", nil) + } + + response := obrimMarkerRoute(markerType, config) + + if status, ok := response["status"].(bool); ok && status { + obrimMarkerLog("success", markerType) + } else { + obrimMarkerLog("failure", markerType) + } + + return response +} + +// Purpose: Validate supported marker generation types. +func obrimMarkerValidateType(markerType string) error { + switch strings.ToLower(strings.TrimSpace(markerType)) { + case obrimMarkerTypeTime, + obrimMarkerTypeRandom, + obrimMarkerTypeEncoded: + return nil + default: + return fmt.Errorf("unsupported marker type") + } +} + +// Purpose: Route execution to the selected marker generation workflow. +func obrimMarkerRoute(markerType string, config map[string]any) map[string]any { + switch strings.ToLower(strings.TrimSpace(markerType)) { + case obrimMarkerTypeTime: + return obrimMarkerTime(config) + + case obrimMarkerTypeRandom: + return obrimMarkerRandom(config) + + case obrimMarkerTypeEncoded: + return obrimMarkerEncoded(config) + + default: + return obrimMarkerBuildResponse(false, "FAILED_INVALID_TYPE", nil) + } +} + +// Purpose: Build standardized success and error responses. +func obrimMarkerBuildResponse(status bool, code string, payload any) map[string]any { + return map[string]any{ + "status": status, + "code": code, + "payload": payload, + } +} + +// Purpose: Generate time-based unique markers. +func obrimMarkerTime(config map[string]any) map[string]any { + epoch, instance, err := obrimMarkerValidateTime(config) + if err != nil { + return obrimMarkerBuildResponse(false, "FAILED_TIME_VALIDATION", nil) + } + + marker, err := obrimMarkerTimeGenerate(epoch, instance) + if err != nil { + return obrimMarkerBuildResponse(false, "FAILED_TIME_GENERATION", nil) + } + + payload := obrimMarkerTimePayload{ + Marker: marker, + Epoch: epoch, + Instance: instance, + } + + return obrimMarkerBuildResponse(true, "SUCCESS_TIME_GENERATED", payload) +} + +// Purpose: Validate time marker configuration parameters. +func obrimMarkerValidateTime(config map[string]any) (int64, string, error) { + epoch := int64(0) + instance := "" + + if value, exists := config["epoch"]; exists { + switch v := value.(type) { + case int64: + epoch = v + case int: + epoch = int64(v) + case float64: + epoch = int64(v) + default: + return 0, "", fmt.Errorf("invalid epoch") + } + + if epoch < 0 { + return 0, "", fmt.Errorf("invalid epoch") + } + } + + if value, exists := config["instance"]; exists { + instance, _ = value.(string) + } + + if strings.TrimSpace(instance) == "" { + instance = "default" + } + + return epoch, instance, nil +} + +// Purpose: Produce the final time-based marker value. +func obrimMarkerTimeGenerate(epoch int64, instance string) (string, error) { + now := time.Now().UnixMilli() + + if epoch > now { + return "", fmt.Errorf("epoch exceeds current time") + } + + timestamp := now - epoch + + sequence := atomic.AddUint64(&obrimMarkerSequence, 1) + + return fmt.Sprintf( + "%X-%s-%X", + timestamp, + strings.ToUpper(instance), + sequence, + ), nil +} + +// Purpose: Generate random collision-resistant markers. +func obrimMarkerRandom(config map[string]any) map[string]any { + length, charset, err := obrimMarkerValidateRandom(config) + if err != nil { + return obrimMarkerBuildResponse(false, "FAILED_RANDOM_VALIDATION", nil) + } + + marker, err := obrimMarkerRandomGenerate(length, charset) + if err != nil { + return obrimMarkerBuildResponse(false, "FAILED_RANDOM_GENERATION", nil) + } + + payload := obrimMarkerRandomPayload{ + Marker: marker, + Length: length, + Charset: charset, + } + + return obrimMarkerBuildResponse(true, "SUCCESS_RANDOM_GENERATED", payload) +} + +// Purpose: Validate random marker configuration parameters. +func obrimMarkerValidateRandom(config map[string]any) (int, string, error) { + length := obrimMarkerDefaultRandomLength + charset := obrimMarkerDefaultCharset + + if value, exists := config["length"]; exists { + switch v := value.(type) { + case int: + length = v + case int64: + length = int(v) + case float64: + length = int(v) + default: + return 0, "", fmt.Errorf("invalid length") + } + } + + if value, exists := config["charset"]; exists { + if text, ok := value.(string); ok { + charset = text + } + } + + if length <= 0 { + return 0, "", fmt.Errorf("invalid length") + } + + if len(charset) == 0 { + return 0, "", fmt.Errorf("invalid charset") + } + + return length, charset, nil +} + +// Purpose: Produce the final random marker value. +func obrimMarkerRandomGenerate(length int, charset string) (string, error) { + buffer := make([]byte, length) + + for index := 0; index < length; index++ { + randomValue := make([]byte, 8) + + if _, err := rand.Read(randomValue); err != nil { + return "", err + } + + position := binary.BigEndian.Uint64(randomValue) % uint64(len(charset)) + buffer[index] = charset[position] + } + + return string(buffer), nil +} + +// Purpose: Generate deterministic encoded markers. +func obrimMarkerEncoded(config map[string]any) map[string]any { + data, length, charset, salt, err := obrimMarkerValidateEncoded(config) + if err != nil { + return obrimMarkerBuildResponse(false, "FAILED_ENCODED_VALIDATION", nil) + } + + marker, err := obrimMarkerEncodedGenerate(data, length, charset, salt) + if err != nil { + return obrimMarkerBuildResponse(false, "FAILED_ENCODED_GENERATION", nil) + } + + payload := obrimMarkerEncodedPayload{ + Marker: marker, + Source: data, + Length: length, + } + + return obrimMarkerBuildResponse(true, "SUCCESS_ENCODED_GENERATED", payload) +} + +// Purpose: Validate encoded marker configuration parameters. +func obrimMarkerValidateEncoded(config map[string]any) (string, int, string, string, error) { + data := "" + length := obrimMarkerDefaultEncodedLength + charset := obrimMarkerDefaultCharset + salt := "" + + if value, exists := config["data"]; exists { + data, _ = value.(string) + } + + if strings.TrimSpace(data) == "" { + return "", 0, "", "", fmt.Errorf("missing data") + } + + if value, exists := config["length"]; exists { + switch v := value.(type) { + case int: + length = v + case int64: + length = int(v) + case float64: + length = int(v) + default: + return "", 0, "", "", fmt.Errorf("invalid length") + } + } + + if value, exists := config["charset"]; exists { + if text, ok := value.(string); ok { + charset = text + } + } + + if value, exists := config["salt"]; exists { + salt, _ = value.(string) + } + + if length <= 0 { + return "", 0, "", "", fmt.Errorf("invalid length") + } + + if len(charset) == 0 { + return "", 0, "", "", fmt.Errorf("invalid charset") + } + + return data, length, charset, salt, nil +} + +// Purpose: Produce the final encoded marker value. +func obrimMarkerEncodedGenerate( + data string, + length int, + charset string, + salt string, +) (string, error) { + hash := sha256.Sum256([]byte(data + salt)) + + output := make([]byte, length) + + for index := 0; index < length; index++ { + hashIndex := index % len(hash) + position := int(math.Abs(float64(hash[hashIndex]))) % len(charset) + output[index] = charset[position] + } + + return string(output), nil +} + +// Purpose: Log utility lifecycle events. +func obrimMarkerLog(event string, message string) { + _ = event + _ = message +} diff --git a/essential/visible/service/helper/progress/progress.go b/essential/visible/service/helper/progress/progress.go index e69de29..43fb4ca 100644 --- a/essential/visible/service/helper/progress/progress.go +++ b/essential/visible/service/helper/progress/progress.go @@ -0,0 +1,335 @@ +package progress + +import ( + "fmt" + "log" + "math" + "strings" + "sync" +) + +const ( + obrimProgressTypeCountable = "countable" + obrimProgressTypeUncountable = "uncountable" + + obrimProgressStateStarted = "started" + obrimProgressStateRunning = "running" + obrimProgressStateCompleted = "completed" + obrimProgressStateCanceled = "canceled" + + obrimProgressSuccessStarted = "SUCCESS_PROGRESS_STARTED" + obrimProgressSuccessRunning = "SUCCESS_PROGRESS_RUNNING" + obrimProgressSuccessCompleted = "SUCCESS_PROGRESS_COMPLETED" + obrimProgressSuccessCanceled = "SUCCESS_PROGRESS_CANCELED" + + obrimProgressFailedMisconfigured = "FAILED_PROGRESS_MISCONFIGURED" + + obrimProgressVisualWidth = 20 +) + +var ( + // obrimProgressMutex protects progress state transitions from corruption. + obrimProgressMutex sync.Mutex +) + +// ObrimProgressConfig defines progress configuration. +type ObrimProgressConfig struct { + State string `json:"state"` + Current int `json:"current,omitempty"` + Target int `json:"target,omitempty"` + Placeholder string `json:"placeholder,omitempty"` +} + +// ObrimProgressResponse defines standardized utility responses. +type ObrimProgressResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload interface{} `json:"payload"` +} + +// ObrimProgressCountablePayload defines countable progress payload. +type ObrimProgressCountablePayload struct { + State string `json:"state"` + Current int `json:"current"` + Target int `json:"target"` + Percentage int `json:"percentage"` + Message string `json:"message"` + Visual string `json:"visual"` +} + +// ObrimProgressUncountablePayload defines uncountable progress payload. +type ObrimProgressUncountablePayload struct { + State string `json:"state"` + Placeholder string `json:"placeholder"` + Message string `json:"message"` + Visual string `json:"visual"` +} + +// ObrimProgress manages lifecycle-aware countable and uncountable progress tracking operations. +func ObrimProgress(progressType string, config ObrimProgressConfig) ObrimProgressResponse { + obrimProgressMutex.Lock() + defer obrimProgressMutex.Unlock() + + if err := obrimProgressValidate(progressType, config); err != nil { + return obrimProgressBuildResponse(false, obrimProgressFailedMisconfigured, nil) + } + + stateCode := obrimProgressHandleState(config.State) + + switch progressType { + case obrimProgressTypeCountable: + return obrimProgressCountable(config, stateCode) + + case obrimProgressTypeUncountable: + return obrimProgressUncountable(config, stateCode) + + default: + return obrimProgressBuildResponse(false, obrimProgressFailedMisconfigured, nil) + } +} + +// obrimProgressValidate validates input parameters and configuration consistency. +func obrimProgressValidate(progressType string, config ObrimProgressConfig) error { + switch progressType { + case obrimProgressTypeCountable: + if config.Target <= 0 { + return fmt.Errorf("invalid target") + } + + case obrimProgressTypeUncountable: + if strings.TrimSpace(config.Placeholder) == "" { + return fmt.Errorf("missing placeholder") + } + + default: + return fmt.Errorf("invalid type") + } + + switch config.State { + case obrimProgressStateStarted, + obrimProgressStateRunning, + obrimProgressStateCompleted, + obrimProgressStateCanceled: + return nil + default: + return fmt.Errorf("invalid state") + } +} + +// obrimProgressHandleState processes lifecycle state transitions. +func obrimProgressHandleState(state string) string { + switch state { + case obrimProgressStateStarted: + log.Printf(`{"feature":"progress","event":"started"}`) + return obrimProgressSuccessStarted + + case obrimProgressStateRunning: + log.Printf(`{"feature":"progress","event":"running"}`) + return obrimProgressSuccessRunning + + case obrimProgressStateCompleted: + log.Printf(`{"feature":"progress","event":"completed"}`) + return obrimProgressSuccessCompleted + + case obrimProgressStateCanceled: + log.Printf(`{"feature":"progress","event":"canceled"}`) + return obrimProgressSuccessCanceled + + default: + return obrimProgressFailedMisconfigured + } +} + +// obrimProgressCountable processes countable progress tracking workflows. +func obrimProgressCountable( + config ObrimProgressConfig, + code string, +) ObrimProgressResponse { + percentage := obrimProgressCalculatePercentage( + config.Current, + config.Target, + ) + + message := obrimProgressGenerateMessage( + obrimProgressTypeCountable, + config.State, + percentage, + "", + ) + + visual := obrimProgressGenerateVisual( + obrimProgressTypeCountable, + percentage, + "", + ) + + payload := ObrimProgressCountablePayload{ + State: config.State, + Current: config.Current, + Target: config.Target, + Percentage: percentage, + Message: message, + Visual: visual, + } + + return obrimProgressBuildResponse(true, code, payload) +} + +// obrimProgressCalculatePercentage calculates and normalizes completion percentages. +func obrimProgressCalculatePercentage(current int, target int) int { + if current < 0 { + return 0 + } + + if target <= 0 { + return 0 + } + + percentage := int( + math.Round( + (float64(current) / float64(target)) * 100, + ), + ) + + if percentage > 100 { + return 100 + } + + if percentage < 0 { + return 0 + } + + return percentage +} + +// obrimProgressUncountable processes uncountable progress tracking workflows. +func obrimProgressUncountable( + config ObrimProgressConfig, + code string, +) ObrimProgressResponse { + placeholder := obrimProgressResolvePlaceholder( + config.Placeholder, + ) + + message := obrimProgressGenerateMessage( + obrimProgressTypeUncountable, + config.State, + 0, + placeholder, + ) + + visual := obrimProgressGenerateVisual( + obrimProgressTypeUncountable, + 0, + placeholder, + ) + + payload := ObrimProgressUncountablePayload{ + State: config.State, + Placeholder: placeholder, + Message: message, + Visual: visual, + } + + return obrimProgressBuildResponse(true, code, payload) +} + +// obrimProgressResolvePlaceholder validates and prepares placeholder activity messages. +func obrimProgressResolvePlaceholder(placeholder string) string { + return strings.TrimSpace(placeholder) +} + +// obrimProgressGenerateMessage generates human-readable progress messages. +func obrimProgressGenerateMessage( + progressType string, + state string, + percentage int, + placeholder string, +) string { + switch progressType { + case obrimProgressTypeCountable: + switch state { + case obrimProgressStateStarted: + return fmt.Sprintf("Progress started (%d%% complete).", percentage) + + case obrimProgressStateRunning: + return fmt.Sprintf("Progress running (%d%% complete).", percentage) + + case obrimProgressStateCompleted: + return "Progress completed successfully." + + case obrimProgressStateCanceled: + return "Progress canceled." + } + + case obrimProgressTypeUncountable: + switch state { + case obrimProgressStateStarted: + return fmt.Sprintf("Activity started: %s", placeholder) + + case obrimProgressStateRunning: + return fmt.Sprintf("Activity running: %s", placeholder) + + case obrimProgressStateCompleted: + return fmt.Sprintf("Activity completed: %s", placeholder) + + case obrimProgressStateCanceled: + return fmt.Sprintf("Activity canceled: %s", placeholder) + } + } + + return "" +} + +// obrimProgressGenerateVisual generates CLI-friendly visual progress representations. +func obrimProgressGenerateVisual( + progressType string, + percentage int, + placeholder string, +) string { + switch progressType { + case obrimProgressTypeCountable: + filled := int( + math.Round( + (float64(percentage) / 100.0) * obrimProgressVisualWidth, + ), + ) + + if filled > obrimProgressVisualWidth { + filled = obrimProgressVisualWidth + } + + if filled < 0 { + filled = 0 + } + + return fmt.Sprintf( + "[%s%s] %d%%", + strings.Repeat("=", filled), + strings.Repeat(" ", obrimProgressVisualWidth-filled), + percentage, + ) + + case obrimProgressTypeUncountable: + return fmt.Sprintf("[...] %s", placeholder) + } + + return "" +} + +// obrimProgressBuildResponse constructs standardized utility response payloads. +func obrimProgressBuildResponse( + status bool, + code string, + payload interface{}, +) ObrimProgressResponse { + if !status { + payload = nil + } + + return ObrimProgressResponse{ + Status: status, + Code: code, + Payload: payload, + } +} diff --git a/essential/visible/service/helper/retriever/retriever.go b/essential/visible/service/helper/retriever/retriever.go index e69de29..a4bb3e4 100644 --- a/essential/visible/service/helper/retriever/retriever.go +++ b/essential/visible/service/helper/retriever/retriever.go @@ -0,0 +1,396 @@ +package retriever + +import ( + "fmt" + "strings" +) + +const ( + obrimRetrieverTypeJson = "json" + obrimRetrieverTypeDb = "db" + + ObrimRetrieverCodeSuccessResource = "SUCCESS_RESOURCE_RETRIEVED" + ObrimRetrieverCodeSuccessPath = "SUCCESS_PATH_RETRIEVED" + ObrimRetrieverCodeSuccessFields = "SUCCESS_FIELDS_RETRIEVED" + + ObrimRetrieverCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE" + ObrimRetrieverCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION" + ObrimRetrieverCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION" + ObrimRetrieverCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND" + ObrimRetrieverCodeFailedPathNotFound = "FAILED_PATH_NOT_FOUND" + ObrimRetrieverCodeFailedFieldNotFound = "FAILED_FIELD_NOT_FOUND" + ObrimRetrieverCodeFailedNotImplemented = "FAILED_NOT_IMPLEMENTED" + ObrimRetrieverCodeFailedProviderUnavailable = "FAILED_PROVIDER_UNAVAILABLE" +) + +// ObrimRetrieverJsonProvider defines a JSON retrieval provider. +type ObrimRetrieverJsonProvider interface { + Retrieve() (any, error) +} + +// ObrimRetrieverResponse defines the standardized utility response. +type ObrimRetrieverResponse struct { + Status bool `json:"status"` + Code string `json:"code"` + Payload map[string]any `json:"payload"` +} + +// obrimRetrieverJsonRegistry stores registered JSON providers. +var obrimRetrieverJsonRegistry = map[string]ObrimRetrieverJsonProvider{} + +// obrimRetrieverDbRegistry stores registered DB providers. +var obrimRetrieverDbRegistry = map[string]any{} + +// ObrimRetriever retrieves data using the configured retrieval type. +func ObrimRetriever(retrievalType string, config map[string]any) map[string]any { + if !obrimRetrieverValidateType(retrievalType) { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedUnsupportedType, + nil, + ) + } + + if errCode := obrimRetrieverValidateConfig(retrievalType, config); errCode != "" { + return obrimRetrieverBuildResponse(false, errCode, nil) + } + + switch retrievalType { + case obrimRetrieverTypeJson: + return obrimRetrieverJson(config) + + case obrimRetrieverTypeDb: + return obrimRetrieverDb(config) + + default: + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedUnsupportedType, + nil, + ) + } +} + +// ObrimRetrieverJsonRegister registers a JSON provider. +func ObrimRetrieverJsonRegister(resource string, provider any) { + jsonProvider, ok := provider.(ObrimRetrieverJsonProvider) + if !ok { + return + } + + obrimRetrieverJsonRegistry[resource] = jsonProvider +} + +// ObrimRetrieverDbRegister registers a DB provider. +func ObrimRetrieverDbRegister(resource string, provider any) { + obrimRetrieverDbRegistry[resource] = provider +} + +// obrimRetrieverValidateType validates the retrieval type. +func obrimRetrieverValidateType(retrievalType string) bool { + switch retrievalType { + case obrimRetrieverTypeJson: + return true + + case obrimRetrieverTypeDb: + return true + + default: + return false + } +} + +// obrimRetrieverValidateConfig validates configuration. +func obrimRetrieverValidateConfig( + retrievalType string, + config map[string]any, +) string { + if config == nil { + return ObrimRetrieverCodeFailedMissingConfig + } + + switch retrievalType { + case obrimRetrieverTypeJson: + return obrimRetrieverJsonValidate(config) + + case obrimRetrieverTypeDb: + return obrimRetrieverDbValidate(config) + + default: + return ObrimRetrieverCodeFailedUnsupportedType + } +} + +// obrimRetrieverResolveProvider resolves a provider. +func obrimRetrieverResolveProvider( + retrievalType string, + resource string, +) (any, bool) { + switch retrievalType { + case obrimRetrieverTypeJson: + provider, ok := obrimRetrieverJsonRegistry[resource] + return provider, ok + + case obrimRetrieverTypeDb: + provider, ok := obrimRetrieverDbRegistry[resource] + return provider, ok + } + + return nil, false +} + +// obrimRetrieverBuildResponse builds a standard response. +func obrimRetrieverBuildResponse( + status bool, + code string, + payload map[string]any, +) map[string]any { + return map[string]any{ + "status": status, + "code": code, + "payload": payload, + } +} + +// obrimRetrieverJson processes JSON retrieval requests. +func obrimRetrieverJson(config map[string]any) map[string]any { + resource := config["resource"].(string) + + providerAny, ok := obrimRetrieverResolveProvider( + obrimRetrieverTypeJson, + resource, + ) + if !ok { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedResourceNotFound, + nil, + ) + } + + provider, ok := providerAny.(ObrimRetrieverJsonProvider) + if !ok { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedProviderUnavailable, + nil, + ) + } + + if fieldsValue, exists := config["fields"]; exists { + return obrimRetrieverJsonFields(provider, fieldsValue) + } + + if pathValue, exists := config["path"]; exists { + return obrimRetrieverJsonPath(provider, pathValue.(string)) + } + + return obrimRetrieverJsonResource(provider) +} + +// obrimRetrieverJsonResource retrieves an entire resource. +func obrimRetrieverJsonResource( + provider ObrimRetrieverJsonProvider, +) map[string]any { + data, err := provider.Retrieve() + if err != nil { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedResourceNotFound, + nil, + ) + } + + return obrimRetrieverBuildResponse( + true, + ObrimRetrieverCodeSuccessResource, + map[string]any{ + "data": data, + }, + ) +} + +// obrimRetrieverJsonPath retrieves a path value. +func obrimRetrieverJsonPath( + provider ObrimRetrieverJsonProvider, + path string, +) map[string]any { + data, err := provider.Retrieve() + if err != nil { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedResourceNotFound, + nil, + ) + } + + value, found := obrimRetrieverJsonResolvePath(data, path) + if !found { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedPathNotFound, + nil, + ) + } + + return obrimRetrieverBuildResponse( + true, + ObrimRetrieverCodeSuccessPath, + map[string]any{ + "data": value, + }, + ) +} + +// obrimRetrieverJsonFields retrieves multiple field values. +func obrimRetrieverJsonFields( + provider ObrimRetrieverJsonProvider, + fieldsValue any, +) map[string]any { + data, err := provider.Retrieve() + if err != nil { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedResourceNotFound, + nil, + ) + } + + fields, ok := obrimRetrieverNormalizeFields(fieldsValue) + if !ok { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedInvalidConfig, + nil, + ) + } + + result := map[string]any{} + + for _, field := range fields { + value, found := obrimRetrieverJsonResolvePath(data, field) + if !found { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedFieldNotFound, + nil, + ) + } + + result[field] = value + } + + return obrimRetrieverBuildResponse( + true, + ObrimRetrieverCodeSuccessFields, + map[string]any{ + "data": result, + }, + ) +} + +// obrimRetrieverJsonValidate validates JSON configuration. +func obrimRetrieverJsonValidate(config map[string]any) string { + resourceValue, exists := config["resource"] + if !exists { + return ObrimRetrieverCodeFailedMissingConfig + } + + resource, ok := resourceValue.(string) + if !ok || strings.TrimSpace(resource) == "" { + return ObrimRetrieverCodeFailedInvalidConfig + } + + _, hasPath := config["path"] + _, hasFields := config["fields"] + + if hasPath && hasFields { + return ObrimRetrieverCodeFailedInvalidConfig + } + + if hasPath { + path, ok := config["path"].(string) + if !ok || strings.TrimSpace(path) == "" { + return ObrimRetrieverCodeFailedInvalidConfig + } + } + + if hasFields { + fields, ok := obrimRetrieverNormalizeFields(config["fields"]) + if !ok || len(fields) == 0 { + return ObrimRetrieverCodeFailedInvalidConfig + } + } + + return "" +} + +// obrimRetrieverDb processes DB retrieval requests. +func obrimRetrieverDb(config map[string]any) map[string]any { + return obrimRetrieverBuildResponse( + false, + ObrimRetrieverCodeFailedNotImplemented, + nil, + ) +} + +// obrimRetrieverDbValidate validates DB configuration. +func obrimRetrieverDbValidate(config map[string]any) string { + return "" +} + +// obrimRetrieverJsonResolvePath resolves a dot path. +func obrimRetrieverJsonResolvePath( + data any, + path string, +) (any, bool) { + current := data + + for _, segment := range strings.Split(path, ".") { + object, ok := current.(map[string]any) + if !ok { + return nil, false + } + + value, exists := object[segment] + if !exists { + return nil, false + } + + current = value + } + + return current, true +} + +// obrimRetrieverNormalizeFields normalizes field inputs. +func obrimRetrieverNormalizeFields( + value any, +) ([]string, bool) { + switch fields := value.(type) { + case []string: + return fields, true + + case []any: + result := make([]string, 0, len(fields)) + + for _, field := range fields { + text, ok := field.(string) + if !ok { + return nil, false + } + + result = append(result, text) + } + + return result, true + + default: + return nil, false + } +} + +// obrimRetrieverError formats internal errors. +func obrimRetrieverError(message string) error { + return fmt.Errorf("%s", message) +} diff --git a/essential/visible/service/helper/status/status.go b/essential/visible/service/helper/status/status.go index e69de29..60635d9 100644 --- a/essential/visible/service/helper/status/status.go +++ b/essential/visible/service/helper/status/status.go @@ -0,0 +1,74 @@ +package status + +import ( + "fmt" + "strings" +) + +const ( + // obrimStatusLabelInfo represents the INFO status label. + obrimStatusLabelInfo = "INFO" + + // obrimStatusLabelWarning represents the WARNING status label. + obrimStatusLabelWarning = "WARNING" + + // obrimStatusLabelSuccess represents the SUCCESS status label. + obrimStatusLabelSuccess = "SUCCESS" + + // obrimStatusLabelError represents the ERROR status label. + obrimStatusLabelError = "ERROR" +) + +// ObrimStatus emits a standardized CLI status message. +func ObrimStatus(label string, message string) { + normalizedLabel := obrimStatusNormalizeLabel(label) + formattedLabel := obrimStatusFormatLabel(normalizedLabel) + indicator := obrimStatusFormatIndicator(normalizedLabel) + finalMessage := obrimStatusBuildMessage(formattedLabel, indicator, message) + + fmt.Println(finalMessage) +} + +// obrimStatusNormalizeLabel normalizes and validates status labels. +func obrimStatusNormalizeLabel(label string) string { + normalizedLabel := strings.ToUpper(strings.TrimSpace(label)) + + switch normalizedLabel { + case obrimStatusLabelInfo: + return obrimStatusLabelInfo + case obrimStatusLabelWarning: + return obrimStatusLabelWarning + case obrimStatusLabelSuccess: + return obrimStatusLabelSuccess + case obrimStatusLabelError: + return obrimStatusLabelError + default: + return obrimStatusLabelInfo + } +} + +// obrimStatusFormatLabel formats semantic status labels. +func obrimStatusFormatLabel(label string) string { + return fmt.Sprintf("[%s]", label) +} + +// obrimStatusFormatIndicator generates visual status indicators. +func obrimStatusFormatIndicator(label string) string { + switch label { + case obrimStatusLabelInfo: + return "ℹ" + case obrimStatusLabelWarning: + return "⚠" + case obrimStatusLabelSuccess: + return "✓" + case obrimStatusLabelError: + return "✖" + default: + return "ℹ" + } +} + +// obrimStatusBuildMessage constructs the final terminal message. +func obrimStatusBuildMessage(label string, indicator string, message string) string { + return fmt.Sprintf("%s %s %s", indicator, label, message) +} diff --git a/go.mod b/go.mod index 75e7cc0..d13e469 100644 --- a/go.mod +++ b/go.mod @@ -1,3 +1,5 @@ module go/module go 1.26.4 + +require github.com/sqids/sqids-go v0.4.1 diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..a02109b --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/sqids/sqids-go v0.4.1 h1:eQKYzmAZbLlRwHeHYPF35QhgxwZHLnlmVj9AkIj/rrw= +github.com/sqids/sqids-go v0.4.1/go.mod h1:EMwHuPQgSNFS0A49jESTfIQS+066XQTVhukrzEPScl8=