Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 0bfc4efec0 | |||
| 811f47b07d | |||
| 35ac79c175 | |||
| 022e672b88 | |||
| 6d0368e8b4 |
@ -0,0 +1,136 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Handler
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide a JSON resource provider for the Retriever utility.
|
||||
| - Embed, parse, and expose a complete JSON resource payload.
|
||||
| - Abstract resource storage details from consumers.
|
||||
|
|
||||
| Guideline:
|
||||
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||
| - Associate the handler with a unique resource identifier.
|
||||
| - Return the complete parsed resource payload only.
|
||||
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||
| - Do not implement business logic within the handler.
|
||||
| - Do not perform querying, transformation, or resource discovery.
|
||||
|
|
||||
| Example:
|
||||
| - retriever.ObrimRetrieverJsonRegister(
|
||||
| "software/metadata",
|
||||
| obrimHandlerProvider{},
|
||||
| )
|
||||
| - response := retriever.ObrimRetriever(
|
||||
| "json",
|
||||
| map[string]any{
|
||||
| "resource": "software/metadata",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"go/module/essential/visible/service/helper/retriever"
|
||||
)
|
||||
|
||||
// constant
|
||||
const (
|
||||
// resource
|
||||
obrimHandlerResourceIdentifier = "framework/glossary" // change identifier if needed
|
||||
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||
obrimHandlerResourceFileExt = ".json" // do not change
|
||||
|
||||
// success
|
||||
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||
|
||||
// fail
|
||||
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||
)
|
||||
|
||||
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||
//
|
||||
//go:embed resource.json
|
||||
var obrimHandlerEmbeddedResource embed.FS
|
||||
|
||||
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||
type obrimHandlerProvider struct{}
|
||||
|
||||
// Retrieve loads and returns the complete parsed resource payload.
|
||||
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||
return obrimHandlerFetch()
|
||||
}
|
||||
|
||||
// init registers the handler during package initialization.
|
||||
func init() {
|
||||
obrimHandlerRegister()
|
||||
}
|
||||
|
||||
// obrimHandlerRegister registers the handler with Retriever.
|
||||
func obrimHandlerRegister() {
|
||||
retriever.ObrimRetrieverJsonRegister(
|
||||
obrimHandlerResourceIdentifier,
|
||||
obrimHandlerProvider{},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||
func obrimHandlerFetch() (any, error) {
|
||||
resource, err := obrimHandlerLoadResource()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := obrimHandlerParseResource(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// obrimHandlerLoadResource loads the embedded resource.
|
||||
func obrimHandlerLoadResource() ([]byte, error) {
|
||||
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||
var payload map[string]any
|
||||
|
||||
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Handler
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide a JSON resource provider for the Retriever utility.
|
||||
| - Embed, parse, and expose a complete JSON resource payload.
|
||||
| - Abstract resource storage details from consumers.
|
||||
|
|
||||
| Guideline:
|
||||
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||
| - Associate the handler with a unique resource identifier.
|
||||
| - Return the complete parsed resource payload only.
|
||||
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||
| - Do not implement business logic within the handler.
|
||||
| - Do not perform querying, transformation, or resource discovery.
|
||||
|
|
||||
| Example:
|
||||
| - retriever.ObrimRetrieverJsonRegister(
|
||||
| "software/metadata",
|
||||
| obrimHandlerProvider{},
|
||||
| )
|
||||
| - response := retriever.ObrimRetriever(
|
||||
| "json",
|
||||
| map[string]any{
|
||||
| "resource": "software/metadata",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"go/module/essential/visible/service/helper/retriever"
|
||||
)
|
||||
|
||||
// constant
|
||||
const (
|
||||
// resource
|
||||
obrimHandlerResourceIdentifier = "framework/metadata" // change identifier if needed
|
||||
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||
obrimHandlerResourceFileExt = ".json" // do not change
|
||||
|
||||
// success
|
||||
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||
|
||||
// fail
|
||||
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||
)
|
||||
|
||||
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||
//
|
||||
//go:embed resource.json
|
||||
var obrimHandlerEmbeddedResource embed.FS
|
||||
|
||||
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||
type obrimHandlerProvider struct{}
|
||||
|
||||
// Retrieve loads and returns the complete parsed resource payload.
|
||||
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||
return obrimHandlerFetch()
|
||||
}
|
||||
|
||||
// init registers the handler during package initialization.
|
||||
func init() {
|
||||
obrimHandlerRegister()
|
||||
}
|
||||
|
||||
// obrimHandlerRegister registers the handler with Retriever.
|
||||
func obrimHandlerRegister() {
|
||||
retriever.ObrimRetrieverJsonRegister(
|
||||
obrimHandlerResourceIdentifier,
|
||||
obrimHandlerProvider{},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||
func obrimHandlerFetch() (any, error) {
|
||||
resource, err := obrimHandlerLoadResource()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := obrimHandlerParseResource(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// obrimHandlerLoadResource loads the embedded resource.
|
||||
func obrimHandlerLoadResource() ([]byte, error) {
|
||||
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||
var payload map[string]any
|
||||
|
||||
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Handler
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide a JSON resource provider for the Retriever utility.
|
||||
| - Embed, parse, and expose a complete JSON resource payload.
|
||||
| - Abstract resource storage details from consumers.
|
||||
|
|
||||
| Guideline:
|
||||
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||
| - Associate the handler with a unique resource identifier.
|
||||
| - Return the complete parsed resource payload only.
|
||||
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||
| - Do not implement business logic within the handler.
|
||||
| - Do not perform querying, transformation, or resource discovery.
|
||||
|
|
||||
| Example:
|
||||
| - retriever.ObrimRetrieverJsonRegister(
|
||||
| "software/metadata",
|
||||
| obrimHandlerProvider{},
|
||||
| )
|
||||
| - response := retriever.ObrimRetriever(
|
||||
| "json",
|
||||
| map[string]any{
|
||||
| "resource": "software/metadata",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"go/module/essential/visible/service/helper/retriever"
|
||||
)
|
||||
|
||||
// constant
|
||||
const (
|
||||
// resource
|
||||
obrimHandlerResourceIdentifier = "software/glossary" // change identifier if needed
|
||||
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||
obrimHandlerResourceFileExt = ".json" // do not change
|
||||
|
||||
// success
|
||||
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||
|
||||
// fail
|
||||
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||
)
|
||||
|
||||
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||
//
|
||||
//go:embed resource.json
|
||||
var obrimHandlerEmbeddedResource embed.FS
|
||||
|
||||
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||
type obrimHandlerProvider struct{}
|
||||
|
||||
// Retrieve loads and returns the complete parsed resource payload.
|
||||
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||
return obrimHandlerFetch()
|
||||
}
|
||||
|
||||
// init registers the handler during package initialization.
|
||||
func init() {
|
||||
obrimHandlerRegister()
|
||||
}
|
||||
|
||||
// obrimHandlerRegister registers the handler with Retriever.
|
||||
func obrimHandlerRegister() {
|
||||
retriever.ObrimRetrieverJsonRegister(
|
||||
obrimHandlerResourceIdentifier,
|
||||
obrimHandlerProvider{},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||
func obrimHandlerFetch() (any, error) {
|
||||
resource, err := obrimHandlerLoadResource()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := obrimHandlerParseResource(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// obrimHandlerLoadResource loads the embedded resource.
|
||||
func obrimHandlerLoadResource() ([]byte, error) {
|
||||
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||
var payload map[string]any
|
||||
|
||||
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
@ -0,0 +1,136 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Handler
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide a JSON resource provider for the Retriever utility.
|
||||
| - Embed, parse, and expose a complete JSON resource payload.
|
||||
| - Abstract resource storage details from consumers.
|
||||
|
|
||||
| Guideline:
|
||||
| - Register the handler through retriever.ObrimRetrieverJsonRegister().
|
||||
| - Associate the handler with a unique resource identifier.
|
||||
| - Return the complete parsed resource payload only.
|
||||
| - Delegate retrieval, traversal, filtering, and selection logic to Retriever.
|
||||
| - Do not implement business logic within the handler.
|
||||
| - Do not perform querying, transformation, or resource discovery.
|
||||
|
|
||||
| Example:
|
||||
| - retriever.ObrimRetrieverJsonRegister(
|
||||
| "software/metadata",
|
||||
| obrimHandlerProvider{},
|
||||
| )
|
||||
| - response := retriever.ObrimRetriever(
|
||||
| "json",
|
||||
| map[string]any{
|
||||
| "resource": "software/metadata",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package handler
|
||||
|
||||
import (
|
||||
"embed"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
|
||||
"go/module/essential/visible/service/helper/retriever"
|
||||
)
|
||||
|
||||
// constant
|
||||
const (
|
||||
// resource
|
||||
obrimHandlerResourceIdentifier = "software/metadata" // change identifier if needed
|
||||
obrimHandlerResourceFileName = "resource" // change file name if needed
|
||||
obrimHandlerResourceFileExt = ".json" // do not change
|
||||
|
||||
// success
|
||||
ObrimHandlerCodeSuccessResourceAvailable = "SUCCESS_RESOURCE_AVAILABLE"
|
||||
|
||||
// fail
|
||||
ObrimHandlerCodeFailedResourceNotFound = "FAILED_RESOURCE_NOT_FOUND"
|
||||
ObrimHandlerCodeFailedInvalidResource = "FAILED_INVALID_RESOURCE"
|
||||
)
|
||||
|
||||
// obrimHandlerEmbeddedResource stores the embedded resource file.
|
||||
//
|
||||
//go:embed resource.json
|
||||
var obrimHandlerEmbeddedResource embed.FS
|
||||
|
||||
// obrimHandlerProvider implements the Retriever JSON provider contract.
|
||||
type obrimHandlerProvider struct{}
|
||||
|
||||
// Retrieve loads and returns the complete parsed resource payload.
|
||||
func (provider obrimHandlerProvider) Retrieve() (any, error) {
|
||||
return obrimHandlerFetch()
|
||||
}
|
||||
|
||||
// init registers the handler during package initialization.
|
||||
func init() {
|
||||
obrimHandlerRegister()
|
||||
}
|
||||
|
||||
// obrimHandlerRegister registers the handler with Retriever.
|
||||
func obrimHandlerRegister() {
|
||||
retriever.ObrimRetrieverJsonRegister(
|
||||
obrimHandlerResourceIdentifier,
|
||||
obrimHandlerProvider{},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHandlerFetch loads, parses, and returns the complete resource payload.
|
||||
func obrimHandlerFetch() (any, error) {
|
||||
resource, err := obrimHandlerLoadResource()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
payload, err := obrimHandlerParseResource(resource)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
|
||||
// obrimHandlerLoadResource loads the embedded resource.
|
||||
func obrimHandlerLoadResource() ([]byte, error) {
|
||||
resource, err := obrimHandlerEmbeddedResource.ReadFile(
|
||||
obrimHandlerResourceFileName + obrimHandlerResourceFileExt,
|
||||
)
|
||||
if err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedResourceNotFound)
|
||||
}
|
||||
|
||||
return resource, nil
|
||||
}
|
||||
|
||||
// obrimHandlerParseResource parses the embedded JSON resource.
|
||||
func obrimHandlerParseResource(resource []byte) (map[string]any, error) {
|
||||
var payload map[string]any
|
||||
|
||||
if err := json.Unmarshal(resource, &payload); err != nil {
|
||||
return nil, errors.New(ObrimHandlerCodeFailedInvalidResource)
|
||||
}
|
||||
|
||||
return payload, nil
|
||||
}
|
||||
@ -0,0 +1,808 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Cipher
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide reusable AES-256 encryption and decryption capabilities
|
||||
| for file-based data processing.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use encrypt-standard for AES-256 encryption without salt metadata.
|
||||
| - Use encrypt-salted for AES-256 encryption with embedded salt,
|
||||
| signature, and version metadata.
|
||||
| - Use decrypt-standard only for files produced by
|
||||
| encrypt-standard.
|
||||
| - Use decrypt-salted only for files produced by
|
||||
| encrypt-salted.
|
||||
| - Always provide a valid AES-256 compatible key.
|
||||
| - Always provide valid input and output file paths.
|
||||
| - Do not manually modify salted file headers, version
|
||||
| information, or embedded salt metadata.
|
||||
| - Treat generated salt as non-secret metadata required for
|
||||
| successful decryption.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimCipher(
|
||||
| "encrypt-standard",
|
||||
| map[string]any{
|
||||
| "key": "<32-byte-key>",
|
||||
| "input": "./plain.txt",
|
||||
| "output": "./encrypted.bin",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimCipher(
|
||||
| "encrypt-salted",
|
||||
| map[string]any{
|
||||
| "key": "<32-byte-key>",
|
||||
| "input": "./plain.txt",
|
||||
| "output": "./encrypted.obc",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimCipher(
|
||||
| "decrypt-standard",
|
||||
| map[string]any{
|
||||
| "key": "<32-byte-key>",
|
||||
| "input": "./encrypted.bin",
|
||||
| "output": "./plain.txt",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimCipher(
|
||||
| "decrypt-salted",
|
||||
| map[string]any{
|
||||
| "key": "<32-byte-key>",
|
||||
| "input": "./encrypted.obc",
|
||||
| "output": "./plain.txt",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package cipher
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/aes"
|
||||
cryptocipher "crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimCipherTypeEncryptStandard = "encrypt-standard"
|
||||
obrimCipherTypeEncryptSalted = "encrypt-salted"
|
||||
obrimCipherTypeDecryptStandard = "decrypt-standard"
|
||||
obrimCipherTypeDecryptSalted = "decrypt-salted"
|
||||
|
||||
obrimCipherAlgorithm = "AES-256"
|
||||
|
||||
obrimCipherFormatSignature = "OBRIMCIPHER"
|
||||
obrimCipherFormatVersion = "1"
|
||||
|
||||
obrimCipherSaltLength = 32
|
||||
obrimCipherNonceLength = 12
|
||||
|
||||
ObrimCipherCodeSuccessEncryptStandard = "SUCCESS_ENCRYPT_STANDARD"
|
||||
ObrimCipherCodeSuccessEncryptSalted = "SUCCESS_ENCRYPT_SALTED"
|
||||
ObrimCipherCodeSuccessDecryptStandard = "SUCCESS_DECRYPT_STANDARD"
|
||||
ObrimCipherCodeSuccessDecryptSalted = "SUCCESS_DECRYPT_SALTED"
|
||||
|
||||
ObrimCipherCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
||||
ObrimCipherCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||
ObrimCipherCodeFailedInvalidKey = "FAILED_INVALID_KEY"
|
||||
ObrimCipherCodeFailedInvalidInputPath = "FAILED_INVALID_INPUT_PATH"
|
||||
ObrimCipherCodeFailedInvalidOutputPath = "FAILED_INVALID_OUTPUT_PATH"
|
||||
ObrimCipherCodeFailedInputAccess = "FAILED_INPUT_ACCESS"
|
||||
ObrimCipherCodeFailedOutputWrite = "FAILED_OUTPUT_WRITE"
|
||||
ObrimCipherCodeFailedEncryption = "FAILED_ENCRYPTION"
|
||||
ObrimCipherCodeFailedDecryption = "FAILED_DECRYPTION"
|
||||
ObrimCipherCodeFailedSaltGeneration = "FAILED_SALT_GENERATION"
|
||||
ObrimCipherCodeFailedSignatureValidation = "FAILED_SIGNATURE_VALIDATION"
|
||||
ObrimCipherCodeFailedVersionValidation = "FAILED_VERSION_VALIDATION"
|
||||
ObrimCipherCodeFailedSaltExtraction = "FAILED_SALT_EXTRACTION"
|
||||
ObrimCipherCodeFailedPayloadExtraction = "FAILED_PAYLOAD_EXTRACTION"
|
||||
)
|
||||
|
||||
// ObrimCipherConfig defines utility configuration.
|
||||
type ObrimCipherConfig struct {
|
||||
Key string `json:"key"`
|
||||
Input string `json:"input"`
|
||||
Output string `json:"output"`
|
||||
}
|
||||
|
||||
// ObrimCipherResponse defines standardized utility response.
|
||||
type ObrimCipherResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimCipherHeader defines encrypted file metadata.
|
||||
type obrimCipherHeader struct {
|
||||
Signature string
|
||||
Version string
|
||||
}
|
||||
|
||||
// ObrimCipher executes encryption and decryption workflows.
|
||||
func ObrimCipher(
|
||||
cipherType string,
|
||||
config map[string]any,
|
||||
) map[string]any {
|
||||
if !obrimCipherValidateType(cipherType) {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInvalidType,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if !obrimCipherValidateConfig(config) {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInvalidConfig,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
key := config["key"].(string)
|
||||
|
||||
if !obrimCipherValidateKey(key) {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInvalidKey,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
inputPath, inputOk := obrimCipherResolvePath(
|
||||
config["input"].(string),
|
||||
)
|
||||
|
||||
if !inputOk {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInvalidInputPath,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
outputPath, outputOk := obrimCipherResolvePath(
|
||||
config["output"].(string),
|
||||
)
|
||||
|
||||
if !outputOk {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInvalidOutputPath,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
inputData, readErr := obrimCipherReadInput(inputPath)
|
||||
if readErr != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInputAccess,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
switch cipherType {
|
||||
case obrimCipherTypeEncryptStandard:
|
||||
return obrimCipherEncryptStandard(
|
||||
key,
|
||||
inputPath,
|
||||
outputPath,
|
||||
inputData,
|
||||
)
|
||||
|
||||
case obrimCipherTypeEncryptSalted:
|
||||
return obrimCipherEncryptSalted(
|
||||
key,
|
||||
inputPath,
|
||||
outputPath,
|
||||
inputData,
|
||||
)
|
||||
|
||||
case obrimCipherTypeDecryptStandard:
|
||||
return obrimCipherDecryptStandard(
|
||||
key,
|
||||
inputPath,
|
||||
outputPath,
|
||||
inputData,
|
||||
)
|
||||
|
||||
case obrimCipherTypeDecryptSalted:
|
||||
return obrimCipherDecryptSalted(
|
||||
key,
|
||||
inputPath,
|
||||
outputPath,
|
||||
inputData,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedInvalidType,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCipherValidateType validates operation type.
|
||||
func obrimCipherValidateType(cipherType string) bool {
|
||||
switch cipherType {
|
||||
case obrimCipherTypeEncryptStandard:
|
||||
return true
|
||||
|
||||
case obrimCipherTypeEncryptSalted:
|
||||
return true
|
||||
|
||||
case obrimCipherTypeDecryptStandard:
|
||||
return true
|
||||
|
||||
case obrimCipherTypeDecryptSalted:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCipherValidateConfig validates configuration.
|
||||
func obrimCipherValidateConfig(config map[string]any) bool {
|
||||
if config == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
keyValue, keyExists := config["key"]
|
||||
if !keyExists {
|
||||
return false
|
||||
}
|
||||
|
||||
inputValue, inputExists := config["input"]
|
||||
if !inputExists {
|
||||
return false
|
||||
}
|
||||
|
||||
outputValue, outputExists := config["output"]
|
||||
if !outputExists {
|
||||
return false
|
||||
}
|
||||
|
||||
key, keyOk := keyValue.(string)
|
||||
if !keyOk || key == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
input, inputOk := inputValue.(string)
|
||||
if !inputOk || input == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
output, outputOk := outputValue.(string)
|
||||
if !outputOk || output == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// obrimCipherValidateKey validates AES-256 key requirements.
|
||||
func obrimCipherValidateKey(key string) bool {
|
||||
return len([]byte(key)) == 32
|
||||
}
|
||||
|
||||
// obrimCipherResolvePath resolves a path to absolute form.
|
||||
func obrimCipherResolvePath(path string) (string, bool) {
|
||||
resolvedPath, err := filepath.Abs(path)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return resolvedPath, true
|
||||
}
|
||||
|
||||
// obrimCipherReadInput reads source file content.
|
||||
func obrimCipherReadInput(path string) ([]byte, error) {
|
||||
return os.ReadFile(path)
|
||||
}
|
||||
|
||||
// obrimCipherWriteOutput writes processed output safely.
|
||||
func obrimCipherWriteOutput(
|
||||
path string,
|
||||
data []byte,
|
||||
) error {
|
||||
var directory = filepath.Dir(path)
|
||||
|
||||
if err := os.MkdirAll(directory, 0755); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
var temporaryFile, createErr = os.CreateTemp(
|
||||
directory,
|
||||
".obrim-cipher-*",
|
||||
)
|
||||
|
||||
if createErr != nil {
|
||||
return createErr
|
||||
}
|
||||
|
||||
var temporaryPath = temporaryFile.Name()
|
||||
|
||||
defer func() {
|
||||
_ = temporaryFile.Close()
|
||||
}()
|
||||
|
||||
if _, err := temporaryFile.Write(data); err != nil {
|
||||
_ = os.Remove(temporaryPath)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := temporaryFile.Sync(); err != nil {
|
||||
_ = os.Remove(temporaryPath)
|
||||
return err
|
||||
}
|
||||
|
||||
if err := temporaryFile.Close(); err != nil {
|
||||
_ = os.Remove(temporaryPath)
|
||||
return err
|
||||
}
|
||||
|
||||
return os.Rename(temporaryPath, path)
|
||||
}
|
||||
|
||||
// obrimCipherBuildResponse builds standardized responses.
|
||||
func obrimCipherBuildResponse(
|
||||
status bool,
|
||||
code string,
|
||||
payload map[string]any,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCipherEncryptStandard encrypts data without salt metadata.
|
||||
func obrimCipherEncryptStandard(
|
||||
key string,
|
||||
inputPath string,
|
||||
outputPath string,
|
||||
inputData []byte,
|
||||
) map[string]any {
|
||||
encryptedData, err := obrimCipherEncryptAES(
|
||||
[]byte(key),
|
||||
inputData,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedEncryption,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if err := obrimCipherWriteOutput(
|
||||
outputPath,
|
||||
encryptedData,
|
||||
); err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedOutputWrite,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
ObrimCipherCodeSuccessEncryptStandard,
|
||||
map[string]any{
|
||||
"operation": obrimCipherTypeEncryptStandard,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCipherGenerateSalt generates a cryptographic salt.
|
||||
func obrimCipherGenerateSalt() ([]byte, error) {
|
||||
var salt = make([]byte, obrimCipherSaltLength)
|
||||
|
||||
_, err := io.ReadFull(rand.Reader, salt)
|
||||
|
||||
return salt, err
|
||||
}
|
||||
|
||||
// obrimCipherBuildHeader builds encrypted file header.
|
||||
func obrimCipherBuildHeader() []byte {
|
||||
var buffer bytes.Buffer
|
||||
|
||||
buffer.WriteString(obrimCipherFormatSignature)
|
||||
buffer.WriteByte(0)
|
||||
|
||||
buffer.WriteString(obrimCipherFormatVersion)
|
||||
buffer.WriteByte(0)
|
||||
|
||||
return buffer.Bytes()
|
||||
}
|
||||
|
||||
// obrimCipherEncryptSalted encrypts data with embedded salt metadata.
|
||||
func obrimCipherEncryptSalted(
|
||||
key string,
|
||||
inputPath string,
|
||||
outputPath string,
|
||||
inputData []byte,
|
||||
) map[string]any {
|
||||
salt, saltErr := obrimCipherGenerateSalt()
|
||||
|
||||
if saltErr != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedSaltGeneration,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
var derivedKey = sha256.Sum256(
|
||||
append([]byte(key), salt...),
|
||||
)
|
||||
|
||||
encryptedData, err := obrimCipherEncryptAES(
|
||||
derivedKey[:],
|
||||
inputData,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedEncryption,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
var header = obrimCipherBuildHeader()
|
||||
|
||||
var outputData []byte
|
||||
outputData = append(outputData, header...)
|
||||
outputData = append(outputData, salt...)
|
||||
outputData = append(outputData, encryptedData...)
|
||||
|
||||
if err := obrimCipherWriteOutput(
|
||||
outputPath,
|
||||
outputData,
|
||||
); err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedOutputWrite,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
ObrimCipherCodeSuccessEncryptSalted,
|
||||
map[string]any{
|
||||
"operation": obrimCipherTypeEncryptSalted,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"format": obrimCipherFormatSignature,
|
||||
"version": obrimCipherFormatVersion,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCipherDecryptStandard decrypts AES-256 encrypted data.
|
||||
func obrimCipherDecryptStandard(
|
||||
key string,
|
||||
inputPath string,
|
||||
outputPath string,
|
||||
inputData []byte,
|
||||
) map[string]any {
|
||||
decryptedData, err := obrimCipherDecryptAES(
|
||||
[]byte(key),
|
||||
inputData,
|
||||
)
|
||||
|
||||
if err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedDecryption,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if err := obrimCipherWriteOutput(
|
||||
outputPath,
|
||||
decryptedData,
|
||||
); err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedOutputWrite,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
ObrimCipherCodeSuccessDecryptStandard,
|
||||
map[string]any{
|
||||
"operation": obrimCipherTypeDecryptStandard,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCipherParseHeader parses encrypted metadata.
|
||||
func obrimCipherParseHeader(
|
||||
data []byte,
|
||||
) (obrimCipherHeader, int, error) {
|
||||
var signatureBytes = []byte(obrimCipherFormatSignature)
|
||||
|
||||
if len(data) < len(signatureBytes)+1 {
|
||||
return obrimCipherHeader{}, 0, errors.New("signature")
|
||||
}
|
||||
|
||||
if !bytes.Equal(
|
||||
data[:len(signatureBytes)],
|
||||
signatureBytes,
|
||||
) {
|
||||
return obrimCipherHeader{}, 0, errors.New("signature")
|
||||
}
|
||||
|
||||
var offset = len(signatureBytes)
|
||||
|
||||
if data[offset] != 0 {
|
||||
return obrimCipherHeader{}, 0, errors.New("signature")
|
||||
}
|
||||
|
||||
offset++
|
||||
|
||||
var versionBytes = []byte(obrimCipherFormatVersion)
|
||||
|
||||
if len(data) < offset+len(versionBytes)+1 {
|
||||
return obrimCipherHeader{}, 0, errors.New("version")
|
||||
}
|
||||
|
||||
if !bytes.Equal(
|
||||
data[offset:offset+len(versionBytes)],
|
||||
versionBytes,
|
||||
) {
|
||||
return obrimCipherHeader{}, 0, errors.New("version")
|
||||
}
|
||||
|
||||
offset += len(versionBytes)
|
||||
|
||||
if data[offset] != 0 {
|
||||
return obrimCipherHeader{}, 0, errors.New("version")
|
||||
}
|
||||
|
||||
offset++
|
||||
|
||||
return obrimCipherHeader{
|
||||
Signature: obrimCipherFormatSignature,
|
||||
Version: obrimCipherFormatVersion,
|
||||
}, offset, nil
|
||||
}
|
||||
|
||||
// obrimCipherExtractSalt extracts embedded salt.
|
||||
func obrimCipherExtractSalt(
|
||||
data []byte,
|
||||
offset int,
|
||||
) ([]byte, error) {
|
||||
if len(data) < offset+obrimCipherSaltLength {
|
||||
return nil, errors.New("salt")
|
||||
}
|
||||
|
||||
return data[offset : offset+obrimCipherSaltLength], nil
|
||||
}
|
||||
|
||||
// obrimCipherExtractPayload extracts encrypted payload.
|
||||
func obrimCipherExtractPayload(
|
||||
data []byte,
|
||||
offset int,
|
||||
) ([]byte, error) {
|
||||
var payloadOffset = offset + obrimCipherSaltLength
|
||||
|
||||
if len(data) <= payloadOffset {
|
||||
return nil, errors.New("payload")
|
||||
}
|
||||
|
||||
return data[payloadOffset:], nil
|
||||
}
|
||||
|
||||
// obrimCipherDecryptSalted decrypts AES-256 encrypted data using extracted salt.
|
||||
func obrimCipherDecryptSalted(
|
||||
key string,
|
||||
inputPath string,
|
||||
outputPath string,
|
||||
inputData []byte,
|
||||
) map[string]any {
|
||||
header, offset, headerErr := obrimCipherParseHeader(
|
||||
inputData,
|
||||
)
|
||||
|
||||
if headerErr != nil {
|
||||
if headerErr.Error() == "signature" {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedSignatureValidation,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedVersionValidation,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
salt, saltErr := obrimCipherExtractSalt(
|
||||
inputData,
|
||||
offset,
|
||||
)
|
||||
|
||||
if saltErr != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedSaltExtraction,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
payload, payloadErr := obrimCipherExtractPayload(
|
||||
inputData,
|
||||
offset,
|
||||
)
|
||||
|
||||
if payloadErr != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedPayloadExtraction,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
var derivedKey = sha256.Sum256(
|
||||
append([]byte(key), salt...),
|
||||
)
|
||||
|
||||
decryptedData, decryptErr := obrimCipherDecryptAES(
|
||||
derivedKey[:],
|
||||
payload,
|
||||
)
|
||||
|
||||
if decryptErr != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedDecryption,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if err := obrimCipherWriteOutput(
|
||||
outputPath,
|
||||
decryptedData,
|
||||
); err != nil {
|
||||
return obrimCipherBuildResponse(
|
||||
false,
|
||||
ObrimCipherCodeFailedOutputWrite,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimCipherBuildResponse(
|
||||
true,
|
||||
ObrimCipherCodeSuccessDecryptSalted,
|
||||
map[string]any{
|
||||
"operation": obrimCipherTypeDecryptSalted,
|
||||
"algorithm": obrimCipherAlgorithm,
|
||||
"format": header.Signature,
|
||||
"version": header.Version,
|
||||
"inputPath": inputPath,
|
||||
"outputPath": outputPath,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCipherEncryptAES performs AES-256 encryption.
|
||||
func obrimCipherEncryptAES(
|
||||
key []byte,
|
||||
data []byte,
|
||||
) ([]byte, error) {
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cryptocipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nonce = make([]byte, obrimCipherNonceLength)
|
||||
|
||||
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var encryptedData = gcm.Seal(
|
||||
nil,
|
||||
nonce,
|
||||
data,
|
||||
nil,
|
||||
)
|
||||
|
||||
var result []byte
|
||||
result = append(result, nonce...)
|
||||
result = append(result, encryptedData...)
|
||||
|
||||
return result, nil
|
||||
}
|
||||
|
||||
// obrimCipherDecryptAES performs AES-256 decryption.
|
||||
func obrimCipherDecryptAES(
|
||||
key []byte,
|
||||
data []byte,
|
||||
) ([]byte, error) {
|
||||
if len(data) < obrimCipherNonceLength {
|
||||
return nil, errors.New("invalid payload")
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cryptocipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var nonce = data[:obrimCipherNonceLength]
|
||||
var payload = data[obrimCipherNonceLength:]
|
||||
|
||||
return gcm.Open(
|
||||
nil,
|
||||
nonce,
|
||||
payload,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCipherMarshalResponse marshals response data.
|
||||
func obrimCipherMarshalResponse(
|
||||
response ObrimCipherResponse,
|
||||
) ([]byte, error) {
|
||||
return json.Marshal(response)
|
||||
}
|
||||
@ -0,0 +1,686 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Codec
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide reusable encoding and decoding capabilities for framework
|
||||
| and software services.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use ObrimCodec() as the public entry point for all codec operations.
|
||||
| - Always provide a valid operation type and configuration map.
|
||||
| - Use supported formats only: base8, base10, base16, base32,
|
||||
| base64, and sqids.
|
||||
| - Validate all inputs before processing encoding or decoding requests.
|
||||
| - Use charset and salt values consistently between encode and decode
|
||||
| operations.
|
||||
| - Use case configuration for non-sqids formats.
|
||||
| - Use length configuration for sqids format.
|
||||
| - Treat empty sqids inputs and outputs as operation failures.
|
||||
| - Consume responses through the standardized status, code, and
|
||||
| payload structure.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimCodec("encode", config)
|
||||
| - ObrimCodec("decode", config)
|
||||
| - ObrimCodec("encode", map[string]any{
|
||||
| "format": "base64",
|
||||
| "data": "example",
|
||||
| "charset": "",
|
||||
| "salt": "",
|
||||
| "case": "lower",
|
||||
| })
|
||||
| - ObrimCodec("decode", map[string]any{
|
||||
| "format": "sqids",
|
||||
| "data": "abc123",
|
||||
| "charset": "abcdefghijklmnopqrstuvwxyz",
|
||||
| "salt": "",
|
||||
| "length": 8,
|
||||
| })
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package codec
|
||||
|
||||
import (
|
||||
"encoding/base32"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/sqids/sqids-go"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimCodecTypeEncode = "encode"
|
||||
obrimCodecTypeDecode = "decode"
|
||||
|
||||
obrimCodecFormatBase8 = "base8"
|
||||
obrimCodecFormatBase10 = "base10"
|
||||
obrimCodecFormatBase16 = "base16"
|
||||
obrimCodecFormatBase32 = "base32"
|
||||
obrimCodecFormatBase64 = "base64"
|
||||
obrimCodecFormatSqids = "sqids"
|
||||
|
||||
obrimCodecCaseLower = "lower"
|
||||
obrimCodecCaseUpper = "upper"
|
||||
|
||||
ObrimCodecCodeSuccessEncode = "SUCCESS_ENCODE"
|
||||
ObrimCodecCodeSuccessDecode = "SUCCESS_DECODE"
|
||||
|
||||
ObrimCodecCodeFailedValidation = "FAILED_VALIDATION"
|
||||
ObrimCodecCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||
ObrimCodecCodeFailedUnsupportedFormat = "FAILED_UNSUPPORTED_FORMAT"
|
||||
ObrimCodecCodeFailedMissingConfiguration = "FAILED_MISSING_CONFIGURATION"
|
||||
ObrimCodecCodeFailedInvalidConfiguration = "FAILED_INVALID_CONFIGURATION"
|
||||
ObrimCodecCodeFailedOperation = "FAILED_OPERATION"
|
||||
)
|
||||
|
||||
// ObrimCodecResponse defines the standardized utility response.
|
||||
type ObrimCodecResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimCodecConfiguration stores validated codec configuration.
|
||||
type obrimCodecConfiguration struct {
|
||||
Format string
|
||||
Data any
|
||||
Charset string
|
||||
Salt string
|
||||
Case string
|
||||
Length int
|
||||
}
|
||||
|
||||
// ObrimCodec validates configuration, dispatches codec operations, and returns structured results.
|
||||
func ObrimCodec(codecType string, config map[string]any) map[string]any {
|
||||
validatedConfig, code := obrimCodecValidation(codecType, config)
|
||||
if code != "" {
|
||||
return obrimCodecBuildResponse(false, code, nil)
|
||||
}
|
||||
|
||||
value, err := obrimCodecDispatch(codecType, validatedConfig)
|
||||
if err != nil {
|
||||
return obrimCodecBuildResponse(
|
||||
false,
|
||||
ObrimCodecCodeFailedOperation,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
var successCode string
|
||||
|
||||
switch codecType {
|
||||
case obrimCodecTypeEncode:
|
||||
successCode = ObrimCodecCodeSuccessEncode
|
||||
|
||||
case obrimCodecTypeDecode:
|
||||
successCode = ObrimCodecCodeSuccessDecode
|
||||
}
|
||||
|
||||
return obrimCodecBuildResponse(
|
||||
true,
|
||||
successCode,
|
||||
map[string]any{
|
||||
"operation": codecType,
|
||||
"format": validatedConfig.Format,
|
||||
"value": value,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimCodecValidation validates required parameters, supported operations, formats, and parameter types.
|
||||
func obrimCodecValidation(
|
||||
codecType string,
|
||||
config map[string]any,
|
||||
) (*obrimCodecConfiguration, string) {
|
||||
if config == nil {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
switch codecType {
|
||||
case obrimCodecTypeEncode:
|
||||
case obrimCodecTypeDecode:
|
||||
default:
|
||||
return nil, ObrimCodecCodeFailedUnsupportedType
|
||||
}
|
||||
|
||||
formatValue, exists := config["format"]
|
||||
if !exists {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
format, ok := formatValue.(string)
|
||||
if !ok || strings.TrimSpace(format) == "" {
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
switch format {
|
||||
case obrimCodecFormatBase8:
|
||||
case obrimCodecFormatBase10:
|
||||
case obrimCodecFormatBase16:
|
||||
case obrimCodecFormatBase32:
|
||||
case obrimCodecFormatBase64:
|
||||
case obrimCodecFormatSqids:
|
||||
default:
|
||||
return nil, ObrimCodecCodeFailedUnsupportedFormat
|
||||
}
|
||||
|
||||
dataValue, exists := config["data"]
|
||||
if !exists {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
switch dataValue.(type) {
|
||||
case string:
|
||||
case []byte:
|
||||
default:
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
charsetValue, exists := config["charset"]
|
||||
if !exists {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
charset, ok := charsetValue.(string)
|
||||
if !ok {
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
saltValue, exists := config["salt"]
|
||||
if !exists {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
salt, ok := saltValue.(string)
|
||||
if !ok {
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
var validatedConfig = &obrimCodecConfiguration{
|
||||
Format: format,
|
||||
Data: dataValue,
|
||||
Charset: charset,
|
||||
Salt: salt,
|
||||
}
|
||||
|
||||
if format == obrimCodecFormatSqids {
|
||||
lengthValue, exists := config["length"]
|
||||
if !exists {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
switch value := lengthValue.(type) {
|
||||
case int:
|
||||
validatedConfig.Length = value
|
||||
|
||||
case int8:
|
||||
validatedConfig.Length = int(value)
|
||||
|
||||
case int16:
|
||||
validatedConfig.Length = int(value)
|
||||
|
||||
case int32:
|
||||
validatedConfig.Length = int(value)
|
||||
|
||||
case int64:
|
||||
validatedConfig.Length = int(value)
|
||||
|
||||
case float64:
|
||||
validatedConfig.Length = int(value)
|
||||
|
||||
default:
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
if validatedConfig.Length < 0 {
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
} else {
|
||||
caseValue, exists := config["case"]
|
||||
if !exists {
|
||||
return nil, ObrimCodecCodeFailedMissingConfiguration
|
||||
}
|
||||
|
||||
caseRule, ok := caseValue.(string)
|
||||
if !ok {
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
switch caseRule {
|
||||
case obrimCodecCaseLower:
|
||||
case obrimCodecCaseUpper:
|
||||
default:
|
||||
return nil, ObrimCodecCodeFailedInvalidConfiguration
|
||||
}
|
||||
|
||||
validatedConfig.Case = caseRule
|
||||
}
|
||||
|
||||
return validatedConfig, ""
|
||||
}
|
||||
|
||||
// obrimCodecDispatch routes validated requests to the appropriate codec implementation.
|
||||
func obrimCodecDispatch(
|
||||
codecType string,
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
switch codecType {
|
||||
case obrimCodecTypeEncode:
|
||||
switch config.Format {
|
||||
case obrimCodecFormatBase8:
|
||||
return obrimCodecEncodeBase8(config)
|
||||
|
||||
case obrimCodecFormatBase10:
|
||||
return obrimCodecEncodeBase10(config)
|
||||
|
||||
case obrimCodecFormatBase16:
|
||||
return obrimCodecEncodeBase16(config)
|
||||
|
||||
case obrimCodecFormatBase32:
|
||||
return obrimCodecEncodeBase32(config)
|
||||
|
||||
case obrimCodecFormatBase64:
|
||||
return obrimCodecEncodeBase64(config)
|
||||
|
||||
case obrimCodecFormatSqids:
|
||||
return obrimCodecEncodeSqids(config)
|
||||
}
|
||||
|
||||
case obrimCodecTypeDecode:
|
||||
switch config.Format {
|
||||
case obrimCodecFormatBase8:
|
||||
return obrimCodecDecodeBase8(config)
|
||||
|
||||
case obrimCodecFormatBase10:
|
||||
return obrimCodecDecodeBase10(config)
|
||||
|
||||
case obrimCodecFormatBase16:
|
||||
return obrimCodecDecodeBase16(config)
|
||||
|
||||
case obrimCodecFormatBase32:
|
||||
return obrimCodecDecodeBase32(config)
|
||||
|
||||
case obrimCodecFormatBase64:
|
||||
return obrimCodecDecodeBase64(config)
|
||||
|
||||
case obrimCodecFormatSqids:
|
||||
return obrimCodecDecodeSqids(config)
|
||||
}
|
||||
}
|
||||
|
||||
return "", obrimCodecError("unsupported dispatch")
|
||||
}
|
||||
|
||||
// obrimCodecBuildResponse builds a standard response.
|
||||
func obrimCodecBuildResponse(
|
||||
status bool,
|
||||
code string,
|
||||
payload map[string]any,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCodecNormalizeInput converts supported input types into string form.
|
||||
func obrimCodecNormalizeInput(data any) 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 {
|
||||
var prefix = charset + salt
|
||||
|
||||
return strings.TrimPrefix(value, prefix)
|
||||
}
|
||||
|
||||
// obrimCodecApplyCase applies output casing.
|
||||
func obrimCodecApplyCase(
|
||||
value string,
|
||||
caseRule string,
|
||||
) string {
|
||||
switch caseRule {
|
||||
case obrimCodecCaseUpper:
|
||||
return strings.ToUpper(value)
|
||||
|
||||
default:
|
||||
return strings.ToLower(value)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase8 encodes data using Base8 encoding.
|
||||
func obrimCodecEncodeBase8(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
var builder strings.Builder
|
||||
|
||||
for _, value := range []byte(input) {
|
||||
builder.WriteString(fmt.Sprintf("%03o", value))
|
||||
}
|
||||
|
||||
return obrimCodecApplyCase(
|
||||
builder.String(),
|
||||
config.Case,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase8 decodes Base8 encoded data.
|
||||
func obrimCodecDecodeBase8(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = strings.ToLower(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
)
|
||||
|
||||
if len(input)%3 != 0 {
|
||||
return "", obrimCodecError("invalid base8 data")
|
||||
}
|
||||
|
||||
var output []byte
|
||||
|
||||
for index := 0; index < len(input); index += 3 {
|
||||
value, err := strconv.ParseUint(
|
||||
input[index:index+3],
|
||||
8,
|
||||
8,
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
output = append(output, byte(value))
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(
|
||||
string(output),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase10 encodes data using Base10 encoding.
|
||||
func obrimCodecEncodeBase10(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
var value = new(big.Int).SetBytes([]byte(input))
|
||||
|
||||
return obrimCodecApplyCase(
|
||||
value.Text(10),
|
||||
config.Case,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase10 decodes Base10 encoded data.
|
||||
func obrimCodecDecodeBase10(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var value = new(big.Int)
|
||||
|
||||
if _, ok := value.SetString(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
10,
|
||||
); !ok {
|
||||
return "", obrimCodecError("invalid base10 data")
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(
|
||||
string(value.Bytes()),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase16 encodes data using Base16 encoding.
|
||||
func obrimCodecEncodeBase16(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
var output = hex.EncodeToString([]byte(input))
|
||||
|
||||
return obrimCodecApplyCase(
|
||||
output,
|
||||
config.Case,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase16 decodes Base16 encoded data.
|
||||
func obrimCodecDecodeBase16(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var output, err = hex.DecodeString(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(
|
||||
string(output),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase32 encodes data using Base32 encoding.
|
||||
func obrimCodecEncodeBase32(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
var output = base32.StdEncoding.EncodeToString(
|
||||
[]byte(input),
|
||||
)
|
||||
|
||||
return obrimCodecApplyCase(
|
||||
output,
|
||||
config.Case,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase32 decodes Base32 encoded data.
|
||||
func obrimCodecDecodeBase32(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var output, err = base32.StdEncoding.DecodeString(
|
||||
strings.ToUpper(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(
|
||||
string(output),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeBase64 encodes data using Base64 encoding.
|
||||
func obrimCodecEncodeBase64(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
var output = base64.StdEncoding.EncodeToString(
|
||||
[]byte(input),
|
||||
)
|
||||
|
||||
return obrimCodecApplyCase(
|
||||
output,
|
||||
config.Case,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecDecodeBase64 decodes Base64 encoded data.
|
||||
func obrimCodecDecodeBase64(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var output, err = base64.StdEncoding.DecodeString(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(
|
||||
string(output),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecEncodeSqids encodes data using Sqids encoding with minimum length enforcement.
|
||||
func obrimCodecEncodeSqids(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var input = obrimCodecApplyTransform(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
)
|
||||
|
||||
var numbers = make([]uint64, 0, len(input))
|
||||
|
||||
for _, value := range []byte(input) {
|
||||
numbers = append(numbers, uint64(value))
|
||||
}
|
||||
|
||||
if len(numbers) == 0 {
|
||||
return "", obrimCodecError("empty sqids input")
|
||||
}
|
||||
|
||||
var sqidsInstance, err = sqids.New(
|
||||
sqids.Options{
|
||||
Alphabet: config.Charset,
|
||||
MinLength: uint8(config.Length),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
return sqidsInstance.Encode(numbers)
|
||||
}
|
||||
|
||||
// obrimCodecDecodeSqids decodes Sqids encoded data using deterministic length validation.
|
||||
func obrimCodecDecodeSqids(
|
||||
config *obrimCodecConfiguration,
|
||||
) (string, error) {
|
||||
var sqidsInstance, err = sqids.New(
|
||||
sqids.Options{
|
||||
Alphabet: config.Charset,
|
||||
MinLength: uint8(config.Length),
|
||||
},
|
||||
)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
var numbers = sqidsInstance.Decode(
|
||||
obrimCodecNormalizeInput(config.Data),
|
||||
)
|
||||
|
||||
if len(numbers) == 0 {
|
||||
return "", obrimCodecError(
|
||||
"invalid or empty sqids data",
|
||||
)
|
||||
}
|
||||
|
||||
var output = make([]byte, 0, len(numbers))
|
||||
|
||||
for _, value := range numbers {
|
||||
if value > 255 {
|
||||
return "", obrimCodecError(
|
||||
"decoded sqids value exceeds byte range",
|
||||
)
|
||||
}
|
||||
|
||||
output = append(output, byte(value))
|
||||
}
|
||||
|
||||
return obrimCodecReverseTransform(
|
||||
string(output),
|
||||
config.Charset,
|
||||
config.Salt,
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimCodecError formats internal errors.
|
||||
func obrimCodecError(message string) error {
|
||||
return errors.New(message)
|
||||
}
|
||||
@ -0,0 +1,329 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Datetime Utility
|
||||
|
|
||||
| Purpose:
|
||||
| - Retrieve the current date and time from either the host system
|
||||
| clock or a trusted framework-managed NTP source and return the
|
||||
| result using a framework-supported date/time format pattern.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use ObrimDatetime() as the primary entry point.
|
||||
| - Always provide a valid datetime type.
|
||||
| - Supported types are "local" and "trusted".
|
||||
| - Always provide the "format" configuration key.
|
||||
| - Use only framework-supported format patterns.
|
||||
| - Expect all successful results to be returned as strings.
|
||||
| - Use "local" when host system time is sufficient.
|
||||
| - Use "trusted" when synchronized network time is required.
|
||||
| - Handle both success and failure response codes.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimDatetime(
|
||||
| "local",
|
||||
| map[string]any{
|
||||
| "format": "yyyy-MM-dd",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimDatetime(
|
||||
| "trusted",
|
||||
| map[string]any{
|
||||
| "format": "yyyy-MM-dd HH:mm:ss z",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package datetime
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"log"
|
||||
"net"
|
||||
"strconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimDatetimeTypeLocal = "local"
|
||||
obrimDatetimeTypeTrusted = "trusted"
|
||||
|
||||
ObrimDatetimeCodeSuccessRetrieved = "SUCCESS_DATETIME_RETRIEVED"
|
||||
|
||||
ObrimDatetimeCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
||||
ObrimDatetimeCodeFailedInvalidFormat = "FAILED_INVALID_FORMAT"
|
||||
ObrimDatetimeCodeFailedTrustedTimeRetrieval = "FAILED_TRUSTED_TIME_RETRIEVAL"
|
||||
)
|
||||
|
||||
const obrimDatetimeTrustedNtpPool = "pool.ntp.org:123"
|
||||
|
||||
// ObrimDatetimePayload defines the success payload structure.
|
||||
type ObrimDatetimePayload struct {
|
||||
Value string `json:"value"`
|
||||
}
|
||||
|
||||
// ObrimDatetimeResponse defines the utility response structure.
|
||||
type ObrimDatetimeResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload *ObrimDatetimePayload `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimDatetimePatternMap stores framework-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 date and time.
|
||||
func ObrimDatetime(
|
||||
datetimeType string,
|
||||
config map[string]any,
|
||||
) map[string]any {
|
||||
log.Println("[datetime] initialization started")
|
||||
|
||||
format, code := obrimDatetimeValidate(datetimeType, config)
|
||||
if code != "" {
|
||||
log.Printf("[datetime] validation failed: %s", code)
|
||||
|
||||
return obrimDatetimeResponse(
|
||||
false,
|
||||
code,
|
||||
"",
|
||||
)
|
||||
}
|
||||
|
||||
var datetimeValue time.Time
|
||||
var err error
|
||||
|
||||
switch datetimeType {
|
||||
case obrimDatetimeTypeLocal:
|
||||
datetimeValue = obrimDatetimeLocal()
|
||||
|
||||
case obrimDatetimeTypeTrusted:
|
||||
datetimeValue, err = obrimDatetimeTrusted()
|
||||
if err != nil {
|
||||
log.Printf(
|
||||
"[datetime] trusted time retrieval failed: %v",
|
||||
err,
|
||||
)
|
||||
|
||||
return obrimDatetimeResponse(
|
||||
false,
|
||||
ObrimDatetimeCodeFailedTrustedTimeRetrieval,
|
||||
"",
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
formattedValue := obrimDatetimeFormatApply(
|
||||
datetimeValue,
|
||||
format,
|
||||
)
|
||||
|
||||
log.Println("[datetime] datetime retrieved successfully")
|
||||
|
||||
return obrimDatetimeResponse(
|
||||
true,
|
||||
ObrimDatetimeCodeSuccessRetrieved,
|
||||
formattedValue,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimDatetimeValidate validates utility inputs.
|
||||
func obrimDatetimeValidate(
|
||||
datetimeType string,
|
||||
config map[string]any,
|
||||
) (string, string) {
|
||||
switch datetimeType {
|
||||
case obrimDatetimeTypeLocal:
|
||||
case obrimDatetimeTypeTrusted:
|
||||
default:
|
||||
return "", ObrimDatetimeCodeFailedInvalidType
|
||||
}
|
||||
|
||||
formatValue, exists := config["format"]
|
||||
if !exists {
|
||||
return "", ObrimDatetimeCodeFailedInvalidFormat
|
||||
}
|
||||
|
||||
format, ok := formatValue.(string)
|
||||
if !ok || format == "" {
|
||||
return "", ObrimDatetimeCodeFailedInvalidFormat
|
||||
}
|
||||
|
||||
if !obrimDatetimePatternValidate(format) {
|
||||
return "", ObrimDatetimeCodeFailedInvalidFormat
|
||||
}
|
||||
|
||||
return format, ""
|
||||
}
|
||||
|
||||
// obrimDatetimePatternValidate validates supported patterns.
|
||||
func obrimDatetimePatternValidate(pattern string) bool {
|
||||
switch pattern {
|
||||
case "timestampsec":
|
||||
return true
|
||||
|
||||
case "timestampmil":
|
||||
return true
|
||||
}
|
||||
|
||||
_, exists := obrimDatetimePatternMap[pattern]
|
||||
|
||||
return exists
|
||||
}
|
||||
|
||||
// obrimDatetimePatternResolve resolves pattern mappings.
|
||||
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, _ := obrimDatetimePatternResolve(pattern)
|
||||
|
||||
return datetimeValue.Format(layout)
|
||||
}
|
||||
|
||||
// obrimDatetimeResponse builds utility responses.
|
||||
func obrimDatetimeResponse(
|
||||
status bool,
|
||||
code string,
|
||||
value string,
|
||||
) map[string]any {
|
||||
if !status {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": code,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": code,
|
||||
"payload": map[string]any{
|
||||
"value": value,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimDatetimeLocal retrieves host system time.
|
||||
func obrimDatetimeLocal() time.Time {
|
||||
return time.Now()
|
||||
}
|
||||
|
||||
// obrimDatetimeTrusted retrieves trusted NTP time.
|
||||
func obrimDatetimeTrusted() (time.Time, error) {
|
||||
return obrimDatetimeTrustedSync()
|
||||
}
|
||||
|
||||
// obrimDatetimeTrustedSync synchronizes trusted time from NTP.
|
||||
func obrimDatetimeTrustedSync() (time.Time, error) {
|
||||
connection, err := net.DialTimeout(
|
||||
"udp",
|
||||
obrimDatetimeTrustedNtpPool,
|
||||
5*time.Second,
|
||||
)
|
||||
if err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
defer connection.Close()
|
||||
|
||||
var 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
|
||||
}
|
||||
|
||||
var response = make([]byte, 48)
|
||||
|
||||
if _, err = connection.Read(response); err != nil {
|
||||
return time.Time{}, err
|
||||
}
|
||||
|
||||
var ntpSeconds = binary.BigEndian.Uint32(
|
||||
response[40:44],
|
||||
)
|
||||
|
||||
var ntpFraction = binary.BigEndian.Uint32(
|
||||
response[44:48],
|
||||
)
|
||||
|
||||
const ntpEpochOffset = 2208988800
|
||||
|
||||
var unixSeconds = int64(ntpSeconds) - ntpEpochOffset
|
||||
|
||||
var nanoseconds = (int64(ntpFraction) * 1e9) >> 32
|
||||
|
||||
if unixSeconds < 0 {
|
||||
return time.Time{}, fmt.Errorf(
|
||||
"invalid ntp timestamp",
|
||||
)
|
||||
}
|
||||
|
||||
return time.Unix(
|
||||
unixSeconds,
|
||||
nanoseconds,
|
||||
).UTC(), nil
|
||||
}
|
||||
@ -0,0 +1,748 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Filesystem
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide reusable, deterministic, and OS-agnostic filesystem
|
||||
| operations for files and directories through a unified execution
|
||||
| interface.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use ObrimFilesystem() as the single public entry point.
|
||||
| - Execute only one filesystem operation per invocation.
|
||||
| - Validate operation type and configuration before execution.
|
||||
| - Resolve filesystem paths through supported strategies.
|
||||
| - Return structured and deterministic results for all executions.
|
||||
| - Remain independent of runtime, CLI, and third-party frameworks.
|
||||
| - Use Go standard library filesystem capabilities whenever possible.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimFilesystem("list", map[string]any{
|
||||
| "strategy": "user",
|
||||
| "recursive": true,
|
||||
| })
|
||||
|
|
||||
| - ObrimFilesystem("check", map[string]any{
|
||||
| "strategy": "custom",
|
||||
| "base": "/tmp",
|
||||
| "name": "example.txt",
|
||||
| })
|
||||
|
|
||||
| - ObrimFilesystem("create", map[string]any{
|
||||
| "entity": "directory",
|
||||
| "strategy": "custom",
|
||||
| "base": "/tmp",
|
||||
| "name": "workspace",
|
||||
| })
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package filesystem
|
||||
|
||||
import (
|
||||
"os"
|
||||
"os/user"
|
||||
"path/filepath"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimFilesystemTypeList = "list"
|
||||
obrimFilesystemTypeCheck = "check"
|
||||
obrimFilesystemTypeCreate = "create"
|
||||
obrimFilesystemTypeDelete = "delete"
|
||||
obrimFilesystemTypePermission = "permission"
|
||||
|
||||
obrimFilesystemEntityFile = "file"
|
||||
obrimFilesystemEntityDirectory = "directory"
|
||||
|
||||
obrimFilesystemStrategyUser = "user"
|
||||
obrimFilesystemStrategyCustom = "custom"
|
||||
|
||||
obrimFilesystemModeRead = "read"
|
||||
obrimFilesystemModeWrite = "write"
|
||||
|
||||
obrimFilesystemCodeSuccessList = "SUCCESS_LIST"
|
||||
obrimFilesystemCodeSuccessCheck = "SUCCESS_CHECK"
|
||||
obrimFilesystemCodeSuccessCreate = "SUCCESS_CREATE"
|
||||
obrimFilesystemCodeSuccessDelete = "SUCCESS_DELETE"
|
||||
obrimFilesystemCodeSuccessPermission = "SUCCESS_PERMISSION"
|
||||
|
||||
obrimFilesystemCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||
obrimFilesystemCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||
obrimFilesystemCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||
obrimFilesystemCodeFailedUnsupportedConfig = "FAILED_UNSUPPORTED_CONFIGURATION"
|
||||
obrimFilesystemCodeFailedPathResolution = "FAILED_PATH_RESOLUTION"
|
||||
obrimFilesystemCodeFailedExecution = "FAILED_EXECUTION"
|
||||
obrimFilesystemCodeFailedPermission = "FAILED_PERMISSION"
|
||||
obrimFilesystemCodeFailedEntityNotFound = "FAILED_ENTITY_NOT_FOUND"
|
||||
obrimFilesystemCodeFailedUnsupportedEntity = "FAILED_UNSUPPORTED_ENTITY"
|
||||
obrimFilesystemCodeFailedInvalidPermission = "FAILED_INVALID_PERMISSION"
|
||||
)
|
||||
|
||||
var obrimFilesystemSupportedKeys = map[string]map[string]bool{
|
||||
obrimFilesystemTypeList: {
|
||||
"entity": true,
|
||||
"strategy": true,
|
||||
"base": true,
|
||||
"name": true,
|
||||
"recursive": true,
|
||||
"include_hidden": true,
|
||||
"sort_by": true,
|
||||
"sort_order": true,
|
||||
"filter_pattern": true,
|
||||
"follow_symlink": true,
|
||||
"absolute_path": true,
|
||||
},
|
||||
obrimFilesystemTypeCheck: {
|
||||
"entity": true,
|
||||
"strategy": true,
|
||||
"base": true,
|
||||
"name": true,
|
||||
"readable": true,
|
||||
"writable": true,
|
||||
},
|
||||
obrimFilesystemTypeCreate: {
|
||||
"entity": true,
|
||||
"strategy": true,
|
||||
"base": true,
|
||||
"name": true,
|
||||
"hidden": true,
|
||||
},
|
||||
obrimFilesystemTypeDelete: {
|
||||
"entity": true,
|
||||
"strategy": true,
|
||||
"base": true,
|
||||
"name": true,
|
||||
"recursive": true,
|
||||
},
|
||||
obrimFilesystemTypePermission: {
|
||||
"strategy": true,
|
||||
"base": true,
|
||||
"name": true,
|
||||
"mode": true,
|
||||
"permission": true,
|
||||
},
|
||||
}
|
||||
|
||||
// ObrimFilesystem executes filesystem operations.
|
||||
func ObrimFilesystem(operationType string, config map[string]any) map[string]any {
|
||||
if code := obrimFilesystemValidate(operationType, config); code != "" {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": code,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
switch operationType {
|
||||
case obrimFilesystemTypeList:
|
||||
return obrimFilesystemList(config)
|
||||
|
||||
case obrimFilesystemTypeCheck:
|
||||
return obrimFilesystemCheck(config)
|
||||
|
||||
case obrimFilesystemTypeCreate:
|
||||
return obrimFilesystemCreate(config)
|
||||
|
||||
case obrimFilesystemTypeDelete:
|
||||
return obrimFilesystemDelete(config)
|
||||
|
||||
case obrimFilesystemTypePermission:
|
||||
return obrimFilesystemPermission(config)
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedUnsupportedType,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemValidate validates operation inputs.
|
||||
func obrimFilesystemValidate(operationType string, config map[string]any) string {
|
||||
if config == nil {
|
||||
return obrimFilesystemCodeFailedMissingConfig
|
||||
}
|
||||
|
||||
if _, exists := obrimFilesystemSupportedKeys[operationType]; !exists {
|
||||
return obrimFilesystemCodeFailedUnsupportedType
|
||||
}
|
||||
|
||||
for key := range config {
|
||||
if !obrimFilesystemSupportedKeys[operationType][key] {
|
||||
return obrimFilesystemCodeFailedUnsupportedConfig
|
||||
}
|
||||
}
|
||||
|
||||
switch operationType {
|
||||
case obrimFilesystemTypeCreate,
|
||||
obrimFilesystemTypeDelete:
|
||||
entity, _ := config["entity"].(string)
|
||||
|
||||
if entity != obrimFilesystemEntityFile &&
|
||||
entity != obrimFilesystemEntityDirectory {
|
||||
return obrimFilesystemCodeFailedUnsupportedEntity
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimFilesystemResolve resolves and normalizes paths.
|
||||
func obrimFilesystemResolve(config map[string]any) (string, error) {
|
||||
var basePath string
|
||||
|
||||
strategy, _ := config["strategy"].(string)
|
||||
|
||||
switch strategy {
|
||||
case "", obrimFilesystemStrategyUser:
|
||||
homePath, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
currentUser, userErr := user.Current()
|
||||
if userErr != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
homePath = currentUser.HomeDir
|
||||
}
|
||||
|
||||
basePath = homePath
|
||||
|
||||
case obrimFilesystemStrategyCustom:
|
||||
basePath, _ = config["base"].(string)
|
||||
|
||||
default:
|
||||
return "", os.ErrInvalid
|
||||
}
|
||||
|
||||
name, _ := config["name"].(string)
|
||||
|
||||
resolvedPath := filepath.Clean(filepath.Join(basePath, name))
|
||||
|
||||
return filepath.Abs(resolvedPath)
|
||||
}
|
||||
|
||||
// obrimFilesystemPayload builds standardized payloads.
|
||||
func obrimFilesystemPayload(payload map[string]any) map[string]any {
|
||||
return payload
|
||||
}
|
||||
|
||||
// obrimFilesystemEntityType determines entity type.
|
||||
func obrimFilesystemEntityType(path string) string {
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
if info.IsDir() {
|
||||
return obrimFilesystemEntityDirectory
|
||||
}
|
||||
|
||||
return obrimFilesystemEntityFile
|
||||
}
|
||||
|
||||
// obrimFilesystemPermissionValidate validates permission values.
|
||||
func obrimFilesystemPermissionValidate(permission string) bool {
|
||||
if permission == "" {
|
||||
return false
|
||||
}
|
||||
|
||||
_, err := strconv.ParseUint(permission, 8, 32)
|
||||
|
||||
return err == nil
|
||||
}
|
||||
|
||||
// obrimFilesystemList executes listing operations.
|
||||
func obrimFilesystemList(config map[string]any) map[string]any {
|
||||
basePath, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
entries, err := obrimFilesystemListTraverse(basePath, config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedExecution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
entries = obrimFilesystemListFilter(entries, config)
|
||||
obrimFilesystemListSort(entries, config)
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessList,
|
||||
"payload": obrimFilesystemPayload(map[string]any{
|
||||
"entries": entries,
|
||||
"total": len(entries),
|
||||
"recursive": config["recursive"] == true,
|
||||
"base_path": basePath,
|
||||
}),
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemListTraverse traverses filesystem entities.
|
||||
func obrimFilesystemListTraverse(basePath string, config map[string]any) ([]map[string]any, error) {
|
||||
var entries []map[string]any
|
||||
|
||||
recursive, _ := config["recursive"].(bool)
|
||||
includeHidden, _ := config["include_hidden"].(bool)
|
||||
absolutePath, _ := config["absolute_path"].(bool)
|
||||
|
||||
walkFunction := func(path string, info os.FileInfo, walkErr error) error {
|
||||
if walkErr != nil {
|
||||
return walkErr
|
||||
}
|
||||
|
||||
if path == basePath {
|
||||
return nil
|
||||
}
|
||||
|
||||
if !includeHidden && strings.HasPrefix(info.Name(), ".") {
|
||||
if info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
entryPath := path
|
||||
|
||||
if !absolutePath {
|
||||
entryPath = info.Name()
|
||||
}
|
||||
|
||||
entries = append(entries, map[string]any{
|
||||
"name": info.Name(),
|
||||
"path": entryPath,
|
||||
"entity": obrimFilesystemEntityType(path),
|
||||
"size": info.Size(),
|
||||
"modified": info.ModTime().UTC().Format(time.RFC3339),
|
||||
})
|
||||
|
||||
if !recursive && info.IsDir() {
|
||||
return filepath.SkipDir
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
err := filepath.Walk(basePath, walkFunction)
|
||||
|
||||
return entries, err
|
||||
}
|
||||
|
||||
// obrimFilesystemListFilter filters listing results.
|
||||
func obrimFilesystemListFilter(entries []map[string]any, config map[string]any) []map[string]any {
|
||||
var filtered []map[string]any
|
||||
|
||||
entity, _ := config["entity"].(string)
|
||||
pattern, _ := config["filter_pattern"].(string)
|
||||
|
||||
for _, entry := range entries {
|
||||
if entity != "" && entry["entity"] != entity {
|
||||
continue
|
||||
}
|
||||
|
||||
if pattern != "" {
|
||||
matched, _ := filepath.Match(pattern, entry["name"].(string))
|
||||
|
||||
if !matched {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
filtered = append(filtered, entry)
|
||||
}
|
||||
|
||||
return filtered
|
||||
}
|
||||
|
||||
// obrimFilesystemListSort sorts listing results.
|
||||
func obrimFilesystemListSort(entries []map[string]any, config map[string]any) {
|
||||
sortBy, _ := config["sort_by"].(string)
|
||||
sortOrder, _ := config["sort_order"].(string)
|
||||
|
||||
sort.Slice(entries, func(i, j int) bool {
|
||||
var result bool
|
||||
|
||||
switch sortBy {
|
||||
case "size":
|
||||
result = entries[i]["size"].(int64) < entries[j]["size"].(int64)
|
||||
|
||||
case "modified":
|
||||
result = entries[i]["modified"].(string) < entries[j]["modified"].(string)
|
||||
|
||||
default:
|
||||
result = entries[i]["name"].(string) < entries[j]["name"].(string)
|
||||
}
|
||||
|
||||
if sortOrder == "desc" {
|
||||
return !result
|
||||
}
|
||||
|
||||
return result
|
||||
})
|
||||
}
|
||||
|
||||
// obrimFilesystemCheck executes validation operations.
|
||||
func obrimFilesystemCheck(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
_, statErr := os.Stat(path)
|
||||
|
||||
exists := statErr == nil
|
||||
entity := ""
|
||||
|
||||
if exists {
|
||||
entity = obrimFilesystemEntityType(path)
|
||||
}
|
||||
|
||||
readable := false
|
||||
writable := false
|
||||
|
||||
if exists {
|
||||
readable, writable = obrimFilesystemCheckAccess(path)
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessCheck,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"exists": exists,
|
||||
"entity": entity,
|
||||
"readable": readable,
|
||||
"writable": writable,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemCheckAccess verifies accessibility.
|
||||
func obrimFilesystemCheckAccess(path string) (bool, bool) {
|
||||
readable := true
|
||||
writable := true
|
||||
|
||||
readFile, readErr := os.Open(path)
|
||||
if readErr != nil {
|
||||
readable = false
|
||||
} else {
|
||||
_ = readFile.Close()
|
||||
}
|
||||
|
||||
writeFile, writeErr := os.OpenFile(path, os.O_WRONLY, 0)
|
||||
if writeErr != nil {
|
||||
writable = false
|
||||
} else {
|
||||
_ = writeFile.Close()
|
||||
}
|
||||
|
||||
return readable, writable
|
||||
}
|
||||
|
||||
// obrimFilesystemCreate executes creation operations.
|
||||
func obrimFilesystemCreate(config map[string]any) map[string]any {
|
||||
entity := config["entity"].(string)
|
||||
|
||||
switch entity {
|
||||
case obrimFilesystemEntityFile:
|
||||
return obrimFilesystemCreateFile(config)
|
||||
|
||||
case obrimFilesystemEntityDirectory:
|
||||
return obrimFilesystemCreateDirectory(config)
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedUnsupportedEntity,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemCreateFile creates a file.
|
||||
func obrimFilesystemCreateFile(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(filepath.Dir(path), 0755); err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedExecution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
file, err := os.Create(path)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedExecution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
_ = file.Close()
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessCreate,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"entity": obrimFilesystemEntityFile,
|
||||
"hidden": config["hidden"] == true,
|
||||
"created": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemCreateDirectory creates a directory.
|
||||
func obrimFilesystemCreateDirectory(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.MkdirAll(path, 0755); err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedExecution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessCreate,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"entity": obrimFilesystemEntityDirectory,
|
||||
"hidden": config["hidden"] == true,
|
||||
"created": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemDelete executes deletion operations.
|
||||
func obrimFilesystemDelete(config map[string]any) map[string]any {
|
||||
entity := config["entity"].(string)
|
||||
|
||||
switch entity {
|
||||
case obrimFilesystemEntityFile:
|
||||
return obrimFilesystemDeleteFile(config)
|
||||
|
||||
case obrimFilesystemEntityDirectory:
|
||||
return obrimFilesystemDeleteDirectory(config)
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedUnsupportedEntity,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemDeleteFile deletes a file.
|
||||
func obrimFilesystemDeleteFile(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
if err := os.Remove(path); err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedExecution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessDelete,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"entity": obrimFilesystemEntityFile,
|
||||
"deleted": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemDeleteDirectory deletes a directory.
|
||||
func obrimFilesystemDeleteDirectory(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
recursive, _ := config["recursive"].(bool)
|
||||
|
||||
if recursive {
|
||||
err = os.RemoveAll(path)
|
||||
} else {
|
||||
err = os.Remove(path)
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedExecution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessDelete,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"entity": obrimFilesystemEntityDirectory,
|
||||
"deleted": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemPermission executes permission operations.
|
||||
func obrimFilesystemPermission(config map[string]any) map[string]any {
|
||||
mode, _ := config["mode"].(string)
|
||||
|
||||
switch mode {
|
||||
case "", obrimFilesystemModeRead:
|
||||
return obrimFilesystemPermissionRead(config)
|
||||
|
||||
case obrimFilesystemModeWrite:
|
||||
return obrimFilesystemPermissionWrite(config)
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedInvalidConfig,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemPermissionRead retrieves permissions.
|
||||
func obrimFilesystemPermissionRead(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
info, err := os.Stat(path)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedEntityNotFound,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessPermission,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"permission": info.Mode().Perm().String(),
|
||||
"mode": obrimFilesystemModeRead,
|
||||
"updated": false,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
// obrimFilesystemPermissionWrite updates permissions.
|
||||
func obrimFilesystemPermissionWrite(config map[string]any) map[string]any {
|
||||
path, err := obrimFilesystemResolve(config)
|
||||
if err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPathResolution,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
permission, _ := config["permission"].(string)
|
||||
|
||||
if !obrimFilesystemPermissionValidate(permission) {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedInvalidPermission,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
value, _ := strconv.ParseUint(permission, 8, 32)
|
||||
|
||||
if err := os.Chmod(path, os.FileMode(value)); err != nil {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": obrimFilesystemCodeFailedPermission,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": obrimFilesystemCodeSuccessPermission,
|
||||
"payload": map[string]any{
|
||||
"path": path,
|
||||
"permission": permission,
|
||||
"mode": obrimFilesystemModeWrite,
|
||||
"updated": true,
|
||||
},
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,594 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Hash
|
||||
|
|
||||
| Purpose:
|
||||
| - Generate and verify cryptographic hash values using standard
|
||||
| or salted hashing methods for integrity verification,
|
||||
| canonical fingerprinting, resource identification,
|
||||
| comparison workflows, and security-oriented hashing
|
||||
| requirements.
|
||||
|
|
||||
| Guideline:
|
||||
| - Call ObrimHash() as the single public entry point.
|
||||
| - Provide exactly two primary parameters: type and config.
|
||||
| - Use type "calculate" to generate hash values.
|
||||
| - Use type "compare" to verify hash values.
|
||||
| - Use mode "standard" for deterministic hashing.
|
||||
| - Use mode "salted" for salt-assisted hashing.
|
||||
| - Use only supported algorithms for the selected mode.
|
||||
| - Preserve generated salt values when future verification
|
||||
| is required.
|
||||
| - Always evaluate the returned status and code values before
|
||||
| consuming payload data.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimHash(
|
||||
| "calculate",
|
||||
| map[string]any{
|
||||
| "mode": "standard",
|
||||
| "algorithm": "sha256",
|
||||
| "value": "example",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimHash(
|
||||
| "calculate",
|
||||
| map[string]any{
|
||||
| "mode": "salted",
|
||||
| "algorithm": "salted_sha512",
|
||||
| "value": "example",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimHash(
|
||||
| "compare",
|
||||
| map[string]any{
|
||||
| "mode": "standard",
|
||||
| "algorithm": "sha256",
|
||||
| "value": "example",
|
||||
| "hash": "<hash>",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimHash(
|
||||
| "compare",
|
||||
| map[string]any{
|
||||
| "mode": "salted",
|
||||
| "algorithm": "salted_sha512",
|
||||
| "value": "example",
|
||||
| "hash": "<hash>",
|
||||
| "salt": "<salt>",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package hash
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimHashTypeCalculate = "calculate"
|
||||
obrimHashTypeCompare = "compare"
|
||||
|
||||
obrimHashModeStandard = "standard"
|
||||
obrimHashModeSalted = "salted"
|
||||
|
||||
obrimHashAlgorithmSHA256 = "sha256"
|
||||
obrimHashAlgorithmSHA512 = "sha512"
|
||||
obrimHashAlgorithmSaltedSHA256 = "salted_sha256"
|
||||
obrimHashAlgorithmSaltedSHA512 = "salted_sha512"
|
||||
|
||||
ObrimHashCodeSuccessHashGenerated = "SUCCESS_HASH_GENERATED"
|
||||
ObrimHashCodeSuccessHashCompared = "SUCCESS_HASH_COMPARED"
|
||||
|
||||
ObrimHashCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||
ObrimHashCodeFailedUnsupportedMode = "FAILED_UNSUPPORTED_MODE"
|
||||
ObrimHashCodeFailedUnsupportedAlgorithm = "FAILED_UNSUPPORTED_ALGORITHM"
|
||||
ObrimHashCodeFailedUnsupportedConfigKey = "FAILED_UNSUPPORTED_CONFIG_KEY"
|
||||
ObrimHashCodeFailedMissingMode = "FAILED_MISSING_MODE"
|
||||
ObrimHashCodeFailedMissingAlgorithm = "FAILED_MISSING_ALGORITHM"
|
||||
ObrimHashCodeFailedMissingValue = "FAILED_MISSING_VALUE"
|
||||
ObrimHashCodeFailedMissingHash = "FAILED_MISSING_HASH"
|
||||
ObrimHashCodeFailedMissingSalt = "FAILED_MISSING_SALT"
|
||||
ObrimHashCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||
ObrimHashCodeFailedSaltNotAllowed = "FAILED_SALT_NOT_ALLOWED"
|
||||
ObrimHashCodeFailedHashGeneration = "FAILED_HASH_GENERATION"
|
||||
ObrimHashCodeFailedSaltGeneration = "FAILED_SALT_GENERATION"
|
||||
)
|
||||
|
||||
// ObrimHashResponse defines the standard utility response.
|
||||
type ObrimHashResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload any `json:"payload"`
|
||||
}
|
||||
|
||||
// ObrimHashConfig defines utility configuration.
|
||||
type ObrimHashConfig struct {
|
||||
Mode string
|
||||
Algorithm string
|
||||
Value string
|
||||
Hash string
|
||||
Salt string
|
||||
}
|
||||
|
||||
// ObrimHashCalculateStandardPayload defines calculate standard payload.
|
||||
type ObrimHashCalculateStandardPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
// ObrimHashCalculateSaltedPayload defines calculate salted payload.
|
||||
type ObrimHashCalculateSaltedPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Hash string `json:"hash"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
// ObrimHashCompareStandardPayload defines compare standard payload.
|
||||
type ObrimHashCompareStandardPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Matched bool `json:"matched"`
|
||||
Hash string `json:"hash"`
|
||||
}
|
||||
|
||||
// ObrimHashCompareSaltedPayload defines compare salted payload.
|
||||
type ObrimHashCompareSaltedPayload struct {
|
||||
Algorithm string `json:"algorithm"`
|
||||
Matched bool `json:"matched"`
|
||||
Hash string `json:"hash"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
// obrimHashAllowedConfigKeys stores 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]any,
|
||||
) map[string]any {
|
||||
if !obrimHashValidateType(hashType) {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedUnsupportedType,
|
||||
)
|
||||
}
|
||||
|
||||
hashConfig, errorCode := obrimHashValidateConfig(
|
||||
hashType,
|
||||
config,
|
||||
)
|
||||
if errorCode != "" {
|
||||
return obrimHashBuildErrorPayload(errorCode)
|
||||
}
|
||||
|
||||
if !obrimHashValidateMode(hashConfig.Mode) {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedUnsupportedMode,
|
||||
)
|
||||
}
|
||||
|
||||
if !obrimHashValidateAlgorithm(
|
||||
hashConfig.Mode,
|
||||
hashConfig.Algorithm,
|
||||
) {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedUnsupportedAlgorithm,
|
||||
)
|
||||
}
|
||||
|
||||
switch hashType {
|
||||
case obrimHashTypeCalculate:
|
||||
return obrimHashCalculate(hashConfig)
|
||||
|
||||
case obrimHashTypeCompare:
|
||||
return obrimHashCompare(hashConfig)
|
||||
|
||||
default:
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedUnsupportedType,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashValidateType validates operation type.
|
||||
func obrimHashValidateType(hashType string) bool {
|
||||
switch hashType {
|
||||
case obrimHashTypeCalculate:
|
||||
return true
|
||||
|
||||
case obrimHashTypeCompare:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashValidateMode validates hashing mode.
|
||||
func obrimHashValidateMode(mode string) bool {
|
||||
switch mode {
|
||||
case obrimHashModeStandard:
|
||||
return true
|
||||
|
||||
case obrimHashModeSalted:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashValidateAlgorithm validates hashing algorithm selection.
|
||||
func obrimHashValidateAlgorithm(
|
||||
mode string,
|
||||
algorithm string,
|
||||
) bool {
|
||||
switch mode {
|
||||
case obrimHashModeStandard:
|
||||
switch algorithm {
|
||||
case obrimHashAlgorithmSHA256:
|
||||
return true
|
||||
|
||||
case obrimHashAlgorithmSHA512:
|
||||
return true
|
||||
}
|
||||
|
||||
case obrimHashModeSalted:
|
||||
switch algorithm {
|
||||
case obrimHashAlgorithmSaltedSHA256:
|
||||
return true
|
||||
|
||||
case obrimHashAlgorithmSaltedSHA512:
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// obrimHashValidateConfig validates configuration keys and values.
|
||||
func obrimHashValidateConfig(
|
||||
hashType string,
|
||||
config map[string]any,
|
||||
) (ObrimHashConfig, string) {
|
||||
var hashConfig ObrimHashConfig
|
||||
|
||||
if config == nil {
|
||||
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
for key := range config {
|
||||
if _, exists := obrimHashAllowedConfigKeys[key]; !exists {
|
||||
return hashConfig, ObrimHashCodeFailedUnsupportedConfigKey
|
||||
}
|
||||
}
|
||||
|
||||
modeValue, ok := config["mode"].(string)
|
||||
if !ok || modeValue == "" {
|
||||
return hashConfig, ObrimHashCodeFailedMissingMode
|
||||
}
|
||||
|
||||
algorithmValue, ok := config["algorithm"].(string)
|
||||
if !ok || algorithmValue == "" {
|
||||
return hashConfig, ObrimHashCodeFailedMissingAlgorithm
|
||||
}
|
||||
|
||||
valueValue, ok := config["value"].(string)
|
||||
if !ok || valueValue == "" {
|
||||
return hashConfig, ObrimHashCodeFailedMissingValue
|
||||
}
|
||||
|
||||
hashConfig.Mode = modeValue
|
||||
hashConfig.Algorithm = algorithmValue
|
||||
hashConfig.Value = valueValue
|
||||
|
||||
if hashValue, exists := config["hash"]; exists {
|
||||
hashText, ok := hashValue.(string)
|
||||
if !ok {
|
||||
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
hashConfig.Hash = hashText
|
||||
}
|
||||
|
||||
if saltValue, exists := config["salt"]; exists {
|
||||
saltText, ok := saltValue.(string)
|
||||
if !ok {
|
||||
return hashConfig, ObrimHashCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
hashConfig.Salt = saltText
|
||||
}
|
||||
|
||||
switch hashType {
|
||||
case obrimHashTypeCalculate:
|
||||
if hashConfig.Mode == obrimHashModeStandard &&
|
||||
hashConfig.Salt != "" {
|
||||
return hashConfig, ObrimHashCodeFailedSaltNotAllowed
|
||||
}
|
||||
|
||||
case obrimHashTypeCompare:
|
||||
if hashConfig.Hash == "" {
|
||||
return hashConfig, ObrimHashCodeFailedMissingHash
|
||||
}
|
||||
|
||||
if hashConfig.Mode == obrimHashModeStandard &&
|
||||
hashConfig.Salt != "" {
|
||||
return hashConfig, ObrimHashCodeFailedSaltNotAllowed
|
||||
}
|
||||
|
||||
if hashConfig.Mode == obrimHashModeSalted &&
|
||||
hashConfig.Salt == "" {
|
||||
return hashConfig, ObrimHashCodeFailedMissingSalt
|
||||
}
|
||||
}
|
||||
|
||||
return hashConfig, ""
|
||||
}
|
||||
|
||||
// obrimHashGenerateHash executes the selected hashing algorithm.
|
||||
func obrimHashGenerateHash(
|
||||
algorithm string,
|
||||
value string,
|
||||
salt string,
|
||||
) (string, bool) {
|
||||
switch algorithm {
|
||||
case obrimHashAlgorithmSHA256:
|
||||
sum := sha256.Sum256([]byte(value))
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
case obrimHashAlgorithmSHA512:
|
||||
sum := sha512.Sum512([]byte(value))
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
case obrimHashAlgorithmSaltedSHA256:
|
||||
sum := sha256.Sum256([]byte(value + salt))
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
case obrimHashAlgorithmSaltedSHA512:
|
||||
sum := sha512.Sum512([]byte(value + salt))
|
||||
return hex.EncodeToString(sum[:]), true
|
||||
|
||||
default:
|
||||
return "", false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashCalculate processes hash generation requests.
|
||||
func obrimHashCalculate(
|
||||
config ObrimHashConfig,
|
||||
) map[string]any {
|
||||
switch config.Mode {
|
||||
case obrimHashModeStandard:
|
||||
return obrimHashCalculateStandard(config)
|
||||
|
||||
case obrimHashModeSalted:
|
||||
return obrimHashCalculateSalted(config)
|
||||
|
||||
default:
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedUnsupportedMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashCalculateStandard generates a deterministic hash.
|
||||
func obrimHashCalculateStandard(
|
||||
config ObrimHashConfig,
|
||||
) map[string]any {
|
||||
hashValue, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
"",
|
||||
)
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedHashGeneration,
|
||||
)
|
||||
}
|
||||
|
||||
payload := ObrimHashCalculateStandardPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Hash: hashValue,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
ObrimHashCodeSuccessHashGenerated,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHashCalculateSalted generates a salted hash and salt pair.
|
||||
func obrimHashCalculateSalted(
|
||||
config ObrimHashConfig,
|
||||
) map[string]any {
|
||||
var activeSalt string
|
||||
|
||||
activeSalt = config.Salt
|
||||
|
||||
if activeSalt == "" {
|
||||
generatedSalt, success := obrimHashGenerateSalt()
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedSaltGeneration,
|
||||
)
|
||||
}
|
||||
|
||||
activeSalt = generatedSalt
|
||||
}
|
||||
|
||||
hashValue, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
activeSalt,
|
||||
)
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedHashGeneration,
|
||||
)
|
||||
}
|
||||
|
||||
payload := ObrimHashCalculateSaltedPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Hash: hashValue,
|
||||
Salt: activeSalt,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
ObrimHashCodeSuccessHashGenerated,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHashGenerateSalt generates a cryptographically secure salt.
|
||||
func obrimHashGenerateSalt() (string, bool) {
|
||||
var saltBytes = make([]byte, 32)
|
||||
|
||||
_, err := rand.Read(saltBytes)
|
||||
if err != nil {
|
||||
return "", false
|
||||
}
|
||||
|
||||
return hex.EncodeToString(saltBytes), true
|
||||
}
|
||||
|
||||
// obrimHashCompare processes hash verification requests.
|
||||
func obrimHashCompare(
|
||||
config ObrimHashConfig,
|
||||
) map[string]any {
|
||||
switch config.Mode {
|
||||
case obrimHashModeStandard:
|
||||
return obrimHashCompareStandard(config)
|
||||
|
||||
case obrimHashModeSalted:
|
||||
return obrimHashCompareSalted(config)
|
||||
|
||||
default:
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedUnsupportedMode,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashCompareStandard regenerates a standard hash for verification.
|
||||
func obrimHashCompareStandard(
|
||||
config ObrimHashConfig,
|
||||
) map[string]any {
|
||||
regeneratedHash, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
"",
|
||||
)
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedHashGeneration,
|
||||
)
|
||||
}
|
||||
|
||||
payload := ObrimHashCompareStandardPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Matched: obrimHashVerify(
|
||||
regeneratedHash,
|
||||
config.Hash,
|
||||
),
|
||||
Hash: config.Hash,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
ObrimHashCodeSuccessHashCompared,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimHashCompareSalted regenerates a salted hash for verification.
|
||||
func obrimHashCompareSalted(
|
||||
config ObrimHashConfig,
|
||||
) map[string]any {
|
||||
regeneratedHash, success := obrimHashGenerateHash(
|
||||
config.Algorithm,
|
||||
config.Value,
|
||||
config.Salt,
|
||||
)
|
||||
if !success {
|
||||
return obrimHashBuildErrorPayload(
|
||||
ObrimHashCodeFailedHashGeneration,
|
||||
)
|
||||
}
|
||||
|
||||
payload := ObrimHashCompareSaltedPayload{
|
||||
Algorithm: config.Algorithm,
|
||||
Matched: obrimHashVerify(
|
||||
regeneratedHash,
|
||||
config.Hash,
|
||||
),
|
||||
Hash: config.Hash,
|
||||
Salt: config.Salt,
|
||||
}
|
||||
|
||||
return obrimHashBuildSuccessPayload(
|
||||
ObrimHashCodeSuccessHashCompared,
|
||||
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 any,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimHashBuildErrorPayload constructs error response payloads.
|
||||
func obrimHashBuildErrorPayload(
|
||||
code string,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": code,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,399 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Key
|
||||
|
|
||||
| Purpose:
|
||||
| - Generate cryptographic key material through a type-driven
|
||||
| orchestration workflow with strict validation, schema isolation,
|
||||
| and algorithm-specific generation routines.
|
||||
|
|
||||
| Guideline:
|
||||
| - Call ObrimKey() as the single public entry point.
|
||||
| - Provide an explicit generation type and configuration.
|
||||
| - Use "symmetric" type for AES key generation.
|
||||
| - Use "asymmetric" type for ECC EdDSA key pair generation.
|
||||
| - Provide only supported configuration fields.
|
||||
| - Complete configuration validation before generation.
|
||||
| - Consume generated key material only from successful responses.
|
||||
| - Check response status and code before accessing payload data.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimKey(
|
||||
| "symmetric",
|
||||
| map[string]any{
|
||||
| "algorithm": "aes",
|
||||
| "key_size": 256,
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimKey(
|
||||
| "asymmetric",
|
||||
| map[string]any{
|
||||
| "algorithm": "ecc",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package key
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/ed25519"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimKeyTypeSymmetric = "symmetric"
|
||||
obrimKeyTypeAsymmetric = "asymmetric"
|
||||
|
||||
obrimKeyAlgorithmAES = "aes"
|
||||
obrimKeyAlgorithmECC = "ecc"
|
||||
|
||||
obrimKeyCurveEdDSA = "eddsa"
|
||||
|
||||
ObrimKeyCodeSuccessSymmetricGenerated = "SUCCESS_SYMMETRIC_KEY_GENERATED"
|
||||
ObrimKeyCodeSuccessAsymmetricGenerated = "SUCCESS_ASYMMETRIC_KEY_GENERATED"
|
||||
|
||||
ObrimKeyCodeFailedMissingType = "FAILED_MISSING_TYPE"
|
||||
ObrimKeyCodeFailedInvalidType = "FAILED_INVALID_TYPE"
|
||||
ObrimKeyCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||
ObrimKeyCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||
ObrimKeyCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||
ObrimKeyCodeFailedUnsupportedField = "FAILED_UNSUPPORTED_CONFIGURATION_FIELD"
|
||||
ObrimKeyCodeFailedMissingAlgorithm = "FAILED_MISSING_ALGORITHM"
|
||||
ObrimKeyCodeFailedInvalidAlgorithm = "FAILED_INVALID_ALGORITHM"
|
||||
ObrimKeyCodeFailedUnsupportedAlgorithm = "FAILED_UNSUPPORTED_ALGORITHM"
|
||||
ObrimKeyCodeFailedMissingKeySize = "FAILED_MISSING_KEY_SIZE"
|
||||
ObrimKeyCodeFailedInvalidKeySize = "FAILED_INVALID_KEY_SIZE"
|
||||
ObrimKeyCodeFailedUnsupportedKeySize = "FAILED_UNSUPPORTED_KEY_SIZE"
|
||||
ObrimKeyCodeFailedKeyGeneration = "FAILED_KEY_GENERATION"
|
||||
)
|
||||
|
||||
type ObrimKeyResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// obrimKeyAllowedSymmetricFields stores allowed symmetric configuration fields.
|
||||
var obrimKeyAllowedSymmetricFields = map[string]struct{}{
|
||||
"algorithm": {},
|
||||
"key_size": {},
|
||||
}
|
||||
|
||||
// obrimKeyAllowedAsymmetricFields stores 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 {
|
||||
normalizedType, errorResponse := obrimKeyValidate(keyType, config)
|
||||
if errorResponse != nil {
|
||||
return errorResponse
|
||||
}
|
||||
|
||||
return obrimKeyDispatch(normalizedType, config)
|
||||
}
|
||||
|
||||
// obrimKeyValidate validates common request structure and schema integrity.
|
||||
func obrimKeyValidate(
|
||||
keyType string,
|
||||
config map[string]any,
|
||||
) (string, map[string]any) {
|
||||
if strings.TrimSpace(keyType) == "" {
|
||||
return "", obrimKeyBuildError(ObrimKeyCodeFailedMissingType)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
return "", obrimKeyBuildError(ObrimKeyCodeFailedMissingConfig)
|
||||
}
|
||||
|
||||
normalizedType := strings.ToLower(strings.TrimSpace(keyType))
|
||||
|
||||
switch normalizedType {
|
||||
case obrimKeyTypeSymmetric:
|
||||
if errorCode := obrimKeyValidateSymmetricConfig(config); errorCode != "" {
|
||||
return "", obrimKeyBuildError(errorCode)
|
||||
}
|
||||
|
||||
case obrimKeyTypeAsymmetric:
|
||||
if errorCode := obrimKeyValidateAsymmetricConfig(config); errorCode != "" {
|
||||
return "", obrimKeyBuildError(errorCode)
|
||||
}
|
||||
|
||||
default:
|
||||
return "", obrimKeyBuildError(ObrimKeyCodeFailedUnsupportedType)
|
||||
}
|
||||
|
||||
return normalizedType, nil
|
||||
}
|
||||
|
||||
// obrimKeyDispatch routes execution to the appropriate cryptographic workflow.
|
||||
func obrimKeyDispatch(
|
||||
keyType string,
|
||||
config map[string]any,
|
||||
) map[string]any {
|
||||
switch keyType {
|
||||
case obrimKeyTypeSymmetric:
|
||||
return obrimKeyGenerateSymmetric(config)
|
||||
|
||||
case obrimKeyTypeAsymmetric:
|
||||
return obrimKeyGenerateAsymmetric(config)
|
||||
|
||||
default:
|
||||
return obrimKeyBuildError(ObrimKeyCodeFailedUnsupportedType)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyValidateSymmetricConfig validates symmetric configuration values.
|
||||
func obrimKeyValidateSymmetricConfig(config map[string]any) string {
|
||||
for field := range config {
|
||||
if _, exists := obrimKeyAllowedSymmetricFields[field]; !exists {
|
||||
return ObrimKeyCodeFailedUnsupportedField
|
||||
}
|
||||
}
|
||||
|
||||
algorithmValue, exists := config["algorithm"]
|
||||
if !exists {
|
||||
return ObrimKeyCodeFailedMissingAlgorithm
|
||||
}
|
||||
|
||||
algorithm, ok := algorithmValue.(string)
|
||||
if !ok {
|
||||
return ObrimKeyCodeFailedInvalidAlgorithm
|
||||
}
|
||||
|
||||
algorithm = strings.ToLower(strings.TrimSpace(algorithm))
|
||||
|
||||
if algorithm != obrimKeyAlgorithmAES {
|
||||
return ObrimKeyCodeFailedUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
keySizeValue, exists := config["key_size"]
|
||||
if !exists {
|
||||
return ObrimKeyCodeFailedMissingKeySize
|
||||
}
|
||||
|
||||
keySize, ok := obrimKeyNormalizeInteger(keySizeValue)
|
||||
if !ok {
|
||||
return ObrimKeyCodeFailedInvalidKeySize
|
||||
}
|
||||
|
||||
switch keySize {
|
||||
case 128, 192, 256:
|
||||
return ""
|
||||
|
||||
default:
|
||||
return ObrimKeyCodeFailedUnsupportedKeySize
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyValidateAsymmetricConfig validates asymmetric configuration values.
|
||||
func obrimKeyValidateAsymmetricConfig(config map[string]any) string {
|
||||
for field := range config {
|
||||
if _, exists := obrimKeyAllowedAsymmetricFields[field]; !exists {
|
||||
return ObrimKeyCodeFailedUnsupportedField
|
||||
}
|
||||
}
|
||||
|
||||
algorithmValue, exists := config["algorithm"]
|
||||
if !exists {
|
||||
return ObrimKeyCodeFailedMissingAlgorithm
|
||||
}
|
||||
|
||||
algorithm, ok := algorithmValue.(string)
|
||||
if !ok {
|
||||
return ObrimKeyCodeFailedInvalidAlgorithm
|
||||
}
|
||||
|
||||
algorithm = strings.ToLower(strings.TrimSpace(algorithm))
|
||||
|
||||
if algorithm != obrimKeyAlgorithmECC {
|
||||
return ObrimKeyCodeFailedUnsupportedAlgorithm
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// 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(
|
||||
ObrimKeyCodeSuccessSymmetricGenerated,
|
||||
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.
|
||||
var obrimKeyMaterial = make([]byte, keySize/8)
|
||||
|
||||
if _, err := rand.Read(obrimKeyMaterial); err != nil {
|
||||
return nil, ObrimKeyCodeFailedKeyGeneration
|
||||
}
|
||||
|
||||
if _, err := aes.NewCipher(obrimKeyMaterial); err != nil {
|
||||
return nil, ObrimKeyCodeFailedKeyGeneration
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"type": obrimKeyTypeSymmetric,
|
||||
"algorithm": obrimKeyAlgorithmAES,
|
||||
"key_size": keySize,
|
||||
"key_material": base64.StdEncoding.EncodeToString(obrimKeyMaterial),
|
||||
"generated_at": time.Now().UTC().Format(time.RFC3339),
|
||||
}, ""
|
||||
}
|
||||
|
||||
// obrimKeyNormalizeSymmetricConfig normalizes symmetric configuration values.
|
||||
func obrimKeyNormalizeSymmetricConfig(
|
||||
config map[string]any,
|
||||
) map[string]any {
|
||||
keySize, _ := obrimKeyNormalizeInteger(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(
|
||||
ObrimKeyCodeSuccessAsymmetricGenerated,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimKeyGenerateECC generates ECC key pair material.
|
||||
func obrimKeyGenerateECC(
|
||||
config map[string]any,
|
||||
) (map[string]any, string) {
|
||||
_ = config
|
||||
|
||||
publicKey, privateKey, err := ed25519.GenerateKey(rand.Reader)
|
||||
if err != nil {
|
||||
return nil, ObrimKeyCodeFailedKeyGeneration
|
||||
}
|
||||
|
||||
return map[string]any{
|
||||
"type": obrimKeyTypeAsymmetric,
|
||||
"algorithm": obrimKeyAlgorithmECC,
|
||||
"curve": obrimKeyCurveEdDSA,
|
||||
"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(
|
||||
code string,
|
||||
payload map[string]any,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": true,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyBuildError constructs standardized error responses.
|
||||
func obrimKeyBuildError(code string) map[string]any {
|
||||
return map[string]any{
|
||||
"status": false,
|
||||
"code": code,
|
||||
"payload": nil,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimKeyNormalizeInteger normalizes integer values.
|
||||
func obrimKeyNormalizeInteger(value any) (int, bool) {
|
||||
switch normalizedValue := value.(type) {
|
||||
case int:
|
||||
return normalizedValue, true
|
||||
|
||||
case int8:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case int16:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case int32:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case int64:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case uint:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case uint8:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case uint16:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case uint32:
|
||||
return int(normalizedValue), true
|
||||
|
||||
case uint64:
|
||||
return int(normalizedValue), true
|
||||
|
||||
default:
|
||||
return 0, false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,259 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Log
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide framework-level structured logging capabilities.
|
||||
| - Persist plaintext log entries using a deterministic and platform-aware storage strategy.
|
||||
| - Enable reusable logging functionality for framework and software components.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use ObrimLog() to write structured log entries.
|
||||
| - Use labels in the format SERVICENAME/UTILITYNAME or SERVICENAME/FEATURENAME.
|
||||
| - Use human-readable messages suitable for operational diagnostics and tracing.
|
||||
| - Allow the utility to manage log file creation and storage path resolution.
|
||||
| - Use append-only logging to preserve historical log records.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimLog("SYSTEM/LOG", "Log utility initialized.")
|
||||
| - ObrimLog("PROJECT/CREATE", "Project created successfully.")
|
||||
| - ObrimLog("USER/AUTHENTICATION", "User authentication completed.")
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package log
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimLogSoftwareName = "softwarelower"
|
||||
obrimLogFileExtension = ".log"
|
||||
)
|
||||
|
||||
// ObrimLog writes a structured log entry.
|
||||
func ObrimLog(label string, message string) {
|
||||
softwareName := obrimLogSoftwareNameResolve()
|
||||
if softwareName == "" {
|
||||
return
|
||||
}
|
||||
|
||||
entry := obrimLogEntryBuild(
|
||||
obrimLogTimestampGenerate(),
|
||||
label,
|
||||
message,
|
||||
)
|
||||
|
||||
filePath := obrimLogFileResolve(softwareName)
|
||||
if filePath == "" {
|
||||
return
|
||||
}
|
||||
|
||||
if err := obrimLogFileEnsure(filePath); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
_ = obrimLogWrite(filePath, entry)
|
||||
}
|
||||
|
||||
// obrimLogSoftwareNameResolve resolves the software name from a local variable.
|
||||
func obrimLogSoftwareNameResolve() string {
|
||||
return strings.TrimSpace(obrimLogSoftwareName)
|
||||
}
|
||||
|
||||
// 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",
|
||||
strings.TrimSpace(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(
|
||||
softwareName string,
|
||||
) string {
|
||||
directoryPath := obrimLogDirectoryResolve(softwareName)
|
||||
if directoryPath == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
directoryPath,
|
||||
softwareName+obrimLogFileExtension,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimLogFileEnsure creates and verifies required log directories and files.
|
||||
func obrimLogFileEnsure(
|
||||
filePath string,
|
||||
) error {
|
||||
directoryPath := filepath.Dir(filePath)
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
// obrimLogDirectoryResolveLinux resolves Linux log directory paths.
|
||||
func obrimLogDirectoryResolveLinux(
|
||||
softwareName string,
|
||||
) string {
|
||||
// Mutable XDG state home environment variable.
|
||||
var xdgStateHome = strings.TrimSpace(
|
||||
os.Getenv("XDG_STATE_HOME"),
|
||||
)
|
||||
|
||||
if xdgStateHome != "" {
|
||||
return filepath.Join(
|
||||
xdgStateHome,
|
||||
"."+softwareName,
|
||||
"logs",
|
||||
)
|
||||
}
|
||||
|
||||
// Mutable user home directory.
|
||||
var homeDirectory string
|
||||
|
||||
userHomeDirectory, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
homeDirectory = userHomeDirectory
|
||||
|
||||
return filepath.Join(
|
||||
homeDirectory,
|
||||
".local",
|
||||
"state",
|
||||
"."+softwareName,
|
||||
"logs",
|
||||
)
|
||||
}
|
||||
|
||||
// obrimLogDirectoryResolveWindows resolves Windows log directory paths.
|
||||
func obrimLogDirectoryResolveWindows(
|
||||
softwareName string,
|
||||
) string {
|
||||
// Mutable LOCALAPPDATA environment variable.
|
||||
var localAppData = strings.TrimSpace(
|
||||
os.Getenv("LOCALAPPDATA"),
|
||||
)
|
||||
|
||||
if localAppData == "" {
|
||||
return ""
|
||||
}
|
||||
|
||||
return filepath.Join(
|
||||
localAppData,
|
||||
softwareName,
|
||||
"Logs",
|
||||
)
|
||||
}
|
||||
|
||||
// obrimLogDirectoryResolveMacOS resolves macOS log directory paths.
|
||||
func obrimLogDirectoryResolveMacOS(
|
||||
softwareName string,
|
||||
) string {
|
||||
// Mutable user home directory.
|
||||
var homeDirectory string
|
||||
|
||||
userHomeDirectory, err := os.UserHomeDir()
|
||||
if err != nil {
|
||||
return ""
|
||||
}
|
||||
|
||||
homeDirectory = userHomeDirectory
|
||||
|
||||
return filepath.Join(
|
||||
homeDirectory,
|
||||
"Library",
|
||||
"Logs",
|
||||
"."+softwareName,
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,479 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Marker
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide a unified utility for generating unique markers through
|
||||
| time-based, random, and encoded generation strategies using a
|
||||
| single standardized entry point.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use ObrimMarker() as the primary entry point for all marker
|
||||
| generation operations.
|
||||
| - Provide a supported marker type and the required configuration
|
||||
| values for the selected generation strategy.
|
||||
| - Consume responses through the standardized status, code, and
|
||||
| payload structure.
|
||||
| - Validate response status before accessing payload data.
|
||||
| - Use time markers for distributed uniqueness requirements.
|
||||
| - Use random markers for collision-resistant identifier generation.
|
||||
| - Use encoded markers for deterministic identifier generation from
|
||||
| source data.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimMarker("time", map[string]any{
|
||||
| "epoch": 1704067200000,
|
||||
| "instance": "NODE01",
|
||||
| })
|
||||
|
|
||||
| - ObrimMarker("random", map[string]any{
|
||||
| "length": 32,
|
||||
| "charset": "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789",
|
||||
| })
|
||||
|
|
||||
| - ObrimMarker("encoded", map[string]any{
|
||||
| "data": "customer-1001",
|
||||
| "length": 24,
|
||||
| "salt": "obrim",
|
||||
| })
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package marker
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"fmt"
|
||||
"strings"
|
||||
"time"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimMarkerTypeTime = "time"
|
||||
obrimMarkerTypeRandom = "random"
|
||||
obrimMarkerTypeEncoded = "encoded"
|
||||
|
||||
ObrimMarkerCodeSuccessTimeGenerated = "SUCCESS_TIME_GENERATED"
|
||||
ObrimMarkerCodeSuccessRandomGenerated = "SUCCESS_RANDOM_GENERATED"
|
||||
ObrimMarkerCodeSuccessEncodedGenerated = "SUCCESS_ENCODED_GENERATED"
|
||||
|
||||
ObrimMarkerCodeFailedUnsupportedType = "FAILED_UNSUPPORTED_TYPE"
|
||||
ObrimMarkerCodeFailedMissingConfig = "FAILED_MISSING_CONFIGURATION"
|
||||
ObrimMarkerCodeFailedInvalidConfig = "FAILED_INVALID_CONFIGURATION"
|
||||
ObrimMarkerCodeFailedGeneration = "FAILED_GENERATION"
|
||||
ObrimMarkerCodeFailedValidation = "FAILED_VALIDATION"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimMarkerDefaultRandomLength = 16
|
||||
obrimMarkerDefaultEncodedLength = 16
|
||||
obrimMarkerDefaultCharset = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
||||
)
|
||||
|
||||
// ObrimMarkerResponse defines the standardized utility response.
|
||||
type ObrimMarkerResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// ObrimMarker routes marker generation requests and returns a standardized marker response.
|
||||
func ObrimMarker(markerType string, config map[string]any) map[string]any {
|
||||
obrimMarkerLog("initialization")
|
||||
|
||||
if !obrimMarkerValidateType(markerType) {
|
||||
obrimMarkerLog("failure")
|
||||
|
||||
return obrimMarkerBuildResponse(
|
||||
false,
|
||||
ObrimMarkerCodeFailedUnsupportedType,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if config == nil {
|
||||
config = map[string]any{}
|
||||
}
|
||||
|
||||
response := obrimMarkerRoute(markerType, config)
|
||||
|
||||
if status, ok := response["status"].(bool); ok && status {
|
||||
obrimMarkerLog("success")
|
||||
} else {
|
||||
obrimMarkerLog("failure")
|
||||
}
|
||||
|
||||
return response
|
||||
}
|
||||
|
||||
// obrimMarkerValidateType validates supported marker generation types.
|
||||
func obrimMarkerValidateType(markerType string) bool {
|
||||
switch strings.TrimSpace(markerType) {
|
||||
case obrimMarkerTypeTime:
|
||||
return true
|
||||
|
||||
case obrimMarkerTypeRandom:
|
||||
return true
|
||||
|
||||
case obrimMarkerTypeEncoded:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimMarkerRoute routes execution to the selected marker generation workflow.
|
||||
func obrimMarkerRoute(
|
||||
markerType string,
|
||||
config map[string]any,
|
||||
) map[string]any {
|
||||
switch markerType {
|
||||
case obrimMarkerTypeTime:
|
||||
return obrimMarkerTime(config)
|
||||
|
||||
case obrimMarkerTypeRandom:
|
||||
return obrimMarkerRandom(config)
|
||||
|
||||
case obrimMarkerTypeEncoded:
|
||||
return obrimMarkerEncoded(config)
|
||||
|
||||
default:
|
||||
return obrimMarkerBuildResponse(
|
||||
false,
|
||||
ObrimMarkerCodeFailedUnsupportedType,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// obrimMarkerBuildResponse builds standardized success and error responses.
|
||||
func obrimMarkerBuildResponse(
|
||||
status bool,
|
||||
code string,
|
||||
payload map[string]any,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimMarkerTime generates time-based unique markers.
|
||||
func obrimMarkerTime(config map[string]any) map[string]any {
|
||||
epoch, instance, code := obrimMarkerValidateTime(config)
|
||||
if code != "" {
|
||||
return obrimMarkerBuildResponse(false, code, nil)
|
||||
}
|
||||
|
||||
marker, err := obrimMarkerTimeGenerate(epoch, instance)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(
|
||||
false,
|
||||
ObrimMarkerCodeFailedGeneration,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimMarkerBuildResponse(
|
||||
true,
|
||||
ObrimMarkerCodeSuccessTimeGenerated,
|
||||
map[string]any{
|
||||
"marker": marker,
|
||||
"epoch": epoch,
|
||||
"instance": instance,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimMarkerValidateTime validates time marker configuration parameters.
|
||||
func obrimMarkerValidateTime(
|
||||
config map[string]any,
|
||||
) (int64, string, string) {
|
||||
epochValue, epochExists := config["epoch"]
|
||||
instanceValue, instanceExists := config["instance"]
|
||||
|
||||
if !epochExists {
|
||||
return 0, "", ObrimMarkerCodeFailedMissingConfig
|
||||
}
|
||||
|
||||
if !instanceExists {
|
||||
return 0, "", ObrimMarkerCodeFailedMissingConfig
|
||||
}
|
||||
|
||||
var epoch int64
|
||||
|
||||
switch value := epochValue.(type) {
|
||||
case int64:
|
||||
epoch = value
|
||||
|
||||
case int:
|
||||
epoch = int64(value)
|
||||
|
||||
case float64:
|
||||
epoch = int64(value)
|
||||
|
||||
default:
|
||||
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
instance, ok := instanceValue.(string)
|
||||
if !ok || strings.TrimSpace(instance) == "" {
|
||||
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
if epoch < 0 {
|
||||
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
return epoch, instance, ""
|
||||
}
|
||||
|
||||
// obrimMarkerTimeGenerate produces the final time-based marker value.
|
||||
func obrimMarkerTimeGenerate(
|
||||
epoch int64,
|
||||
instance string,
|
||||
) (string, error) {
|
||||
now := time.Now().UnixMilli()
|
||||
|
||||
if epoch > now {
|
||||
return "", obrimMarkerError("invalid epoch")
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"%X%s",
|
||||
now-epoch,
|
||||
strings.ToUpper(instance),
|
||||
), nil
|
||||
}
|
||||
|
||||
// obrimMarkerRandom generates random collision-resistant markers.
|
||||
func obrimMarkerRandom(config map[string]any) map[string]any {
|
||||
length, charset, code := obrimMarkerValidateRandom(config)
|
||||
if code != "" {
|
||||
return obrimMarkerBuildResponse(false, code, nil)
|
||||
}
|
||||
|
||||
marker, err := obrimMarkerRandomGenerate(length, charset)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(
|
||||
false,
|
||||
ObrimMarkerCodeFailedGeneration,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimMarkerBuildResponse(
|
||||
true,
|
||||
ObrimMarkerCodeSuccessRandomGenerated,
|
||||
map[string]any{
|
||||
"marker": marker,
|
||||
"length": length,
|
||||
"charset": charset,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimMarkerValidateRandom validates random marker configuration parameters.
|
||||
func obrimMarkerValidateRandom(
|
||||
config map[string]any,
|
||||
) (int, string, string) {
|
||||
var length = obrimMarkerDefaultRandomLength
|
||||
var charset = obrimMarkerDefaultCharset
|
||||
|
||||
if value, exists := config["length"]; exists {
|
||||
switch typedValue := value.(type) {
|
||||
case int:
|
||||
length = typedValue
|
||||
|
||||
case int64:
|
||||
length = int(typedValue)
|
||||
|
||||
case float64:
|
||||
length = int(typedValue)
|
||||
|
||||
default:
|
||||
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := config["charset"]; exists {
|
||||
typedValue, ok := value.(string)
|
||||
if !ok {
|
||||
return 0, "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
charset = typedValue
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return 0, "", ObrimMarkerCodeFailedValidation
|
||||
}
|
||||
|
||||
if strings.TrimSpace(charset) == "" {
|
||||
return 0, "", ObrimMarkerCodeFailedValidation
|
||||
}
|
||||
|
||||
return length, charset, ""
|
||||
}
|
||||
|
||||
// obrimMarkerRandomGenerate produces the final random marker value.
|
||||
func obrimMarkerRandomGenerate(
|
||||
length int,
|
||||
charset string,
|
||||
) (string, error) {
|
||||
var buffer = make([]byte, length)
|
||||
var randomBuffer = make([]byte, length)
|
||||
|
||||
_, err := rand.Read(randomBuffer)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
for index := range buffer {
|
||||
buffer[index] = charset[int(randomBuffer[index])%len(charset)]
|
||||
}
|
||||
|
||||
return string(buffer), nil
|
||||
}
|
||||
|
||||
// obrimMarkerEncoded generates deterministic encoded markers.
|
||||
func obrimMarkerEncoded(config map[string]any) map[string]any {
|
||||
data, length, charset, salt, code := obrimMarkerValidateEncoded(config)
|
||||
if code != "" {
|
||||
return obrimMarkerBuildResponse(false, code, nil)
|
||||
}
|
||||
|
||||
marker, err := obrimMarkerEncodedGenerate(
|
||||
data,
|
||||
length,
|
||||
charset,
|
||||
salt,
|
||||
)
|
||||
if err != nil {
|
||||
return obrimMarkerBuildResponse(
|
||||
false,
|
||||
ObrimMarkerCodeFailedGeneration,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimMarkerBuildResponse(
|
||||
true,
|
||||
ObrimMarkerCodeSuccessEncodedGenerated,
|
||||
map[string]any{
|
||||
"marker": marker,
|
||||
"source": data,
|
||||
"length": length,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimMarkerValidateEncoded validates encoded marker configuration parameters.
|
||||
func obrimMarkerValidateEncoded(
|
||||
config map[string]any,
|
||||
) (string, int, string, string, string) {
|
||||
dataValue, exists := config["data"]
|
||||
if !exists {
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedMissingConfig
|
||||
}
|
||||
|
||||
data, ok := dataValue.(string)
|
||||
if !ok || strings.TrimSpace(data) == "" {
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
var length = obrimMarkerDefaultEncodedLength
|
||||
var charset = obrimMarkerDefaultCharset
|
||||
var salt string
|
||||
|
||||
if value, exists := config["length"]; exists {
|
||||
switch typedValue := value.(type) {
|
||||
case int:
|
||||
length = typedValue
|
||||
|
||||
case int64:
|
||||
length = int(typedValue)
|
||||
|
||||
case float64:
|
||||
length = int(typedValue)
|
||||
|
||||
default:
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
}
|
||||
|
||||
if value, exists := config["charset"]; exists {
|
||||
typedValue, ok := value.(string)
|
||||
if !ok {
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
charset = typedValue
|
||||
}
|
||||
|
||||
if value, exists := config["salt"]; exists {
|
||||
typedValue, ok := value.(string)
|
||||
if !ok {
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
salt = typedValue
|
||||
}
|
||||
|
||||
if length <= 0 {
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedValidation
|
||||
}
|
||||
|
||||
if strings.TrimSpace(charset) == "" {
|
||||
return "", 0, "", "", ObrimMarkerCodeFailedValidation
|
||||
}
|
||||
|
||||
return data, length, charset, salt, ""
|
||||
}
|
||||
|
||||
// obrimMarkerEncodedGenerate produces the final encoded marker value.
|
||||
func obrimMarkerEncodedGenerate(
|
||||
data string,
|
||||
length int,
|
||||
charset string,
|
||||
salt string,
|
||||
) (string, error) {
|
||||
var hash = sha256.Sum256([]byte(data + salt))
|
||||
var output = make([]byte, length)
|
||||
|
||||
for index := 0; index < length; index++ {
|
||||
output[index] = charset[int(hash[index%len(hash)])%len(charset)]
|
||||
}
|
||||
|
||||
return string(output), nil
|
||||
}
|
||||
|
||||
// obrimMarkerLog logs utility lifecycle events.
|
||||
func obrimMarkerLog(event string) {
|
||||
_ = event
|
||||
}
|
||||
|
||||
// obrimMarkerError formats internal errors.
|
||||
func obrimMarkerError(message string) error {
|
||||
return fmt.Errorf("%s", message)
|
||||
}
|
||||
@ -0,0 +1,460 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Progress
|
||||
|
|
||||
| Purpose:
|
||||
| - Manage, monitor, and report task progress through standardized
|
||||
| lifecycle-aware progress tracking.
|
||||
|
|
||||
| Guideline:
|
||||
| - Use "countable" type when progress can be measured using current
|
||||
| and target numeric values.
|
||||
| - Use "uncountable" type when progress is activity-based and cannot
|
||||
| be represented by a completion percentage.
|
||||
| - Always provide a valid lifecycle state before executing the utility.
|
||||
| - Provide "current" and "target" values for countable progress
|
||||
| tracking operations.
|
||||
| - Provide a non-empty "placeholder" value for uncountable progress
|
||||
| tracking operations.
|
||||
| - Use returned payload data for CLI rendering, status reporting,
|
||||
| and workflow monitoring.
|
||||
| - Check the returned status and code before consuming payload data.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimProgress(
|
||||
| "countable",
|
||||
| map[string]any{
|
||||
| "state": "running",
|
||||
| "current": 45,
|
||||
| "target": 100,
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - ObrimProgress(
|
||||
| "uncountable",
|
||||
| map[string]any{
|
||||
| "state": "running",
|
||||
| "placeholder": "Processing records...",
|
||||
| },
|
||||
| )
|
||||
|
|
||||
| - result := ObrimProgress(
|
||||
| "countable",
|
||||
| map[string]any{
|
||||
| "state": "completed",
|
||||
| "current": 100,
|
||||
| "target": 100,
|
||||
| },
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package progress
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strings"
|
||||
"sync"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimProgressTypeCountable = "countable"
|
||||
obrimProgressTypeUncountable = "uncountable"
|
||||
|
||||
obrimProgressStateStarted = "started"
|
||||
obrimProgressStateRunning = "running"
|
||||
obrimProgressStateCompleted = "completed"
|
||||
obrimProgressStateCanceled = "canceled"
|
||||
|
||||
ObrimProgressCodeSuccessStarted = "SUCCESS_PROGRESS_STARTED"
|
||||
ObrimProgressCodeSuccessRunning = "SUCCESS_PROGRESS_RUNNING"
|
||||
ObrimProgressCodeSuccessCompleted = "SUCCESS_PROGRESS_COMPLETED"
|
||||
ObrimProgressCodeSuccessCanceled = "SUCCESS_PROGRESS_CANCELED"
|
||||
|
||||
ObrimProgressCodeFailedMisconfigured = "FAILED_PROGRESS_MISCONFIGURED"
|
||||
|
||||
obrimProgressVisualLength = 20
|
||||
)
|
||||
|
||||
// obrimProgressStateMutex prevents state corruption during rapid updates.
|
||||
var obrimProgressStateMutex sync.Mutex
|
||||
|
||||
// ObrimProgressCountablePayload defines countable progress payload data.
|
||||
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 data.
|
||||
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 map[string]any) map[string]any {
|
||||
obrimProgressStateMutex.Lock()
|
||||
defer obrimProgressStateMutex.Unlock()
|
||||
|
||||
if !obrimProgressValidate(progressType, config) {
|
||||
return obrimProgressBuildResponse(
|
||||
false,
|
||||
ObrimProgressCodeFailedMisconfigured,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
code := obrimProgressHandleState(config["state"].(string))
|
||||
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
return obrimProgressCountable(config, code)
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
return obrimProgressUncountable(config, code)
|
||||
}
|
||||
|
||||
return obrimProgressBuildResponse(
|
||||
false,
|
||||
ObrimProgressCodeFailedMisconfigured,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimProgressValidate validates input parameters and configuration consistency.
|
||||
func obrimProgressValidate(
|
||||
progressType string,
|
||||
config map[string]any,
|
||||
) bool {
|
||||
if config == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
stateValue, exists := config["state"]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
state, ok := stateValue.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
switch state {
|
||||
case obrimProgressStateStarted,
|
||||
obrimProgressStateRunning,
|
||||
obrimProgressStateCompleted,
|
||||
obrimProgressStateCanceled:
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
currentValue, currentExists := config["current"]
|
||||
targetValue, targetExists := config["target"]
|
||||
|
||||
if !currentExists || !targetExists {
|
||||
return false
|
||||
}
|
||||
|
||||
current, currentOk := currentValue.(int)
|
||||
target, targetOk := targetValue.(int)
|
||||
|
||||
if !currentOk || !targetOk {
|
||||
return false
|
||||
}
|
||||
|
||||
if target <= 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
_ = current
|
||||
|
||||
return true
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
placeholderValue, exists := config["placeholder"]
|
||||
if !exists {
|
||||
return false
|
||||
}
|
||||
|
||||
placeholder, ok := placeholderValue.(string)
|
||||
if !ok {
|
||||
return false
|
||||
}
|
||||
|
||||
return strings.TrimSpace(placeholder) != ""
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
// obrimProgressHandleState processes lifecycle state transitions.
|
||||
func obrimProgressHandleState(state string) string {
|
||||
switch state {
|
||||
case obrimProgressStateStarted:
|
||||
return ObrimProgressCodeSuccessStarted
|
||||
|
||||
case obrimProgressStateRunning:
|
||||
return ObrimProgressCodeSuccessRunning
|
||||
|
||||
case obrimProgressStateCompleted:
|
||||
return ObrimProgressCodeSuccessCompleted
|
||||
|
||||
case obrimProgressStateCanceled:
|
||||
return ObrimProgressCodeSuccessCanceled
|
||||
}
|
||||
|
||||
return ObrimProgressCodeFailedMisconfigured
|
||||
}
|
||||
|
||||
// obrimProgressGenerateMessage generates human-readable progress messages.
|
||||
func obrimProgressGenerateMessage(
|
||||
progressType string,
|
||||
state string,
|
||||
payload map[string]any,
|
||||
) string {
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
percentage := payload["percentage"].(int)
|
||||
|
||||
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 fmt.Sprintf(
|
||||
"Progress completed (%d%% complete).",
|
||||
percentage,
|
||||
)
|
||||
|
||||
case obrimProgressStateCanceled:
|
||||
return fmt.Sprintf(
|
||||
"Progress canceled (%d%% complete).",
|
||||
percentage,
|
||||
)
|
||||
}
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
placeholder := payload["placeholder"].(string)
|
||||
|
||||
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,
|
||||
payload map[string]any,
|
||||
) string {
|
||||
switch progressType {
|
||||
case obrimProgressTypeCountable:
|
||||
percentage := payload["percentage"].(int)
|
||||
|
||||
filled := int(math.Round(
|
||||
(float64(percentage) / 100.0) * float64(obrimProgressVisualLength),
|
||||
))
|
||||
|
||||
if filled > obrimProgressVisualLength {
|
||||
filled = obrimProgressVisualLength
|
||||
}
|
||||
|
||||
if filled < 0 {
|
||||
filled = 0
|
||||
}
|
||||
|
||||
return fmt.Sprintf(
|
||||
"[%s%s] %d%%",
|
||||
strings.Repeat("=", filled),
|
||||
strings.Repeat(" ", obrimProgressVisualLength-filled),
|
||||
percentage,
|
||||
)
|
||||
|
||||
case obrimProgressTypeUncountable:
|
||||
return "[....................]"
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimProgressBuildResponse constructs standardized utility response payloads.
|
||||
func obrimProgressBuildResponse(
|
||||
status bool,
|
||||
code string,
|
||||
payload any,
|
||||
) map[string]any {
|
||||
return map[string]any{
|
||||
"status": status,
|
||||
"code": code,
|
||||
"payload": payload,
|
||||
}
|
||||
}
|
||||
|
||||
// obrimProgressCountable processes countable progress tracking workflows.
|
||||
func obrimProgressCountable(
|
||||
config map[string]any,
|
||||
code string,
|
||||
) map[string]any {
|
||||
current := config["current"].(int)
|
||||
target := config["target"].(int)
|
||||
state := config["state"].(string)
|
||||
|
||||
percentage := obrimProgressCalculatePercentage(
|
||||
current,
|
||||
target,
|
||||
)
|
||||
|
||||
payloadData := map[string]any{
|
||||
"percentage": percentage,
|
||||
}
|
||||
|
||||
payload := ObrimProgressCountablePayload{
|
||||
State: state,
|
||||
Current: current,
|
||||
Target: target,
|
||||
Percentage: percentage,
|
||||
Message: obrimProgressGenerateMessage(
|
||||
obrimProgressTypeCountable,
|
||||
state,
|
||||
payloadData,
|
||||
),
|
||||
Visual: obrimProgressGenerateVisual(
|
||||
obrimProgressTypeCountable,
|
||||
payloadData,
|
||||
),
|
||||
}
|
||||
|
||||
return obrimProgressBuildResponse(
|
||||
true,
|
||||
code,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimProgressCalculatePercentage calculates and normalizes completion percentages.
|
||||
func obrimProgressCalculatePercentage(
|
||||
current int,
|
||||
target int,
|
||||
) int {
|
||||
if current < 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 map[string]any,
|
||||
code string,
|
||||
) map[string]any {
|
||||
state := config["state"].(string)
|
||||
|
||||
placeholder := obrimProgressResolvePlaceholder(
|
||||
config["placeholder"].(string),
|
||||
)
|
||||
|
||||
payloadData := map[string]any{
|
||||
"placeholder": placeholder,
|
||||
}
|
||||
|
||||
payload := ObrimProgressUncountablePayload{
|
||||
State: state,
|
||||
Placeholder: placeholder,
|
||||
Message: obrimProgressGenerateMessage(
|
||||
obrimProgressTypeUncountable,
|
||||
state,
|
||||
payloadData,
|
||||
),
|
||||
Visual: obrimProgressGenerateVisual(
|
||||
obrimProgressTypeUncountable,
|
||||
payloadData,
|
||||
),
|
||||
}
|
||||
|
||||
return obrimProgressBuildResponse(
|
||||
true,
|
||||
code,
|
||||
payload,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimProgressResolvePlaceholder validates and prepares placeholder activity messages.
|
||||
func obrimProgressResolvePlaceholder(
|
||||
placeholder string,
|
||||
) string {
|
||||
return strings.TrimSpace(placeholder)
|
||||
}
|
||||
@ -0,0 +1,491 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Retriever
|
||||
|
|
||||
| Purpose:
|
||||
| - Retrieve data from supported resource providers through a unified
|
||||
| retrieval interface while abstracting provider-specific
|
||||
| implementation details from consumers.
|
||||
|
|
||||
| Guideline:
|
||||
|
|
||||
| ObrimRetriever()
|
||||
| - Use for unified resource retrieval operations.
|
||||
| - Select a supported retrieval type before execution.
|
||||
| - Provide configuration according to the selected retrieval type.
|
||||
| - Use resource retrieval for complete resource payloads.
|
||||
| - Use path retrieval for individual nested values.
|
||||
| - Use field retrieval for multiple selected values.
|
||||
| - Do not provide both path and fields in the same request.
|
||||
|
|
||||
| ObrimRetrieverJsonRegister()
|
||||
| - Register JSON providers before retrieval operations.
|
||||
| - Use unique resource identifiers for provider registration.
|
||||
| - Register one provider per resource identifier.
|
||||
|
|
||||
| ObrimRetrieverDbRegister()
|
||||
| - Register database providers using unique resource identifiers.
|
||||
| - Reserve registration for future database retrieval support.
|
||||
| - Maintain resource-to-provider mappings through registration.
|
||||
|
|
||||
| Example:
|
||||
|
|
||||
| ObrimRetriever()
|
||||
| - ObrimRetriever("json", map[string]any{
|
||||
| "resource": "metadata",
|
||||
| })
|
||||
|
|
||||
| - ObrimRetriever("json", map[string]any{
|
||||
| "resource": "metadata",
|
||||
| "path": "software.name",
|
||||
| })
|
||||
|
|
||||
| - ObrimRetriever("json", map[string]any{
|
||||
| "resource": "metadata",
|
||||
| "fields": []string{
|
||||
| "software.name",
|
||||
| "software.version",
|
||||
| },
|
||||
| })
|
||||
|
|
||||
| ObrimRetrieverJsonRegister()
|
||||
| - ObrimRetrieverJsonRegister(
|
||||
| "metadata",
|
||||
| metadataProvider,
|
||||
| )
|
||||
|
|
||||
| ObrimRetrieverDbRegister()
|
||||
| - ObrimRetrieverDbRegister(
|
||||
| "database",
|
||||
| databaseProvider,
|
||||
| )
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package retriever
|
||||
|
||||
import (
|
||||
"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"
|
||||
)
|
||||
|
||||
// ObrimRetrieverJsonProviderRegistry stores registered JSON providers.
|
||||
var ObrimRetrieverJsonProviderRegistry = map[string]any{}
|
||||
|
||||
// ObrimRetrieverDbProviderRegistry stores registered database providers.
|
||||
var ObrimRetrieverDbProviderRegistry = map[string]any{}
|
||||
|
||||
// ObrimRetrieverResponse defines the standardized utility response structure.
|
||||
type ObrimRetrieverResponse struct {
|
||||
Status bool `json:"status"`
|
||||
Code string `json:"code"`
|
||||
Payload map[string]any `json:"payload"`
|
||||
}
|
||||
|
||||
// ObrimRetriever retrieves data using a unified retrieval interface.
|
||||
func ObrimRetriever(retrievalType string, config map[string]any) map[string]any {
|
||||
if !obrimRetrieverValidateType(retrievalType) {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
obrimRetrieverCodeFailedUnsupportedType,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
validationCode := obrimRetrieverValidateConfig(retrievalType, config)
|
||||
|
||||
if validationCode != "" {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
validationCode,
|
||||
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) {
|
||||
if strings.TrimSpace(resource) == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ObrimRetrieverJsonProviderRegistry[resource] = provider
|
||||
}
|
||||
|
||||
// ObrimRetrieverDbRegister registers a database provider.
|
||||
func ObrimRetrieverDbRegister(resource string, provider any) {
|
||||
if strings.TrimSpace(resource) == "" {
|
||||
return
|
||||
}
|
||||
|
||||
ObrimRetrieverDbProviderRegistry[resource] = provider
|
||||
}
|
||||
|
||||
// obrimRetrieverValidateType validates a retrieval type.
|
||||
func obrimRetrieverValidateType(retrievalType string) bool {
|
||||
switch retrievalType {
|
||||
case obrimRetrieverTypeJson:
|
||||
return true
|
||||
|
||||
case obrimRetrieverTypeDb:
|
||||
return true
|
||||
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimRetrieverValidateConfig validates retrieval 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 by resource identifier.
|
||||
func obrimRetrieverResolveProvider(
|
||||
retrievalType string,
|
||||
resource string,
|
||||
) (any, bool) {
|
||||
switch retrievalType {
|
||||
case obrimRetrieverTypeJson:
|
||||
provider, exists := ObrimRetrieverJsonProviderRegistry[resource]
|
||||
return provider, exists
|
||||
|
||||
case obrimRetrieverTypeDb:
|
||||
provider, exists := ObrimRetrieverDbProviderRegistry[resource]
|
||||
return provider, exists
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimRetrieverBuildResponse builds a standardized utility 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)
|
||||
|
||||
provider, exists := obrimRetrieverResolveProvider(
|
||||
obrimRetrieverTypeJson,
|
||||
resource,
|
||||
)
|
||||
|
||||
if !exists {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
obrimRetrieverCodeFailedResourceNotFound,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
if fields, exists := config["fields"]; exists {
|
||||
return obrimRetrieverJsonFields(provider, fields)
|
||||
}
|
||||
|
||||
if path, exists := config["path"]; exists {
|
||||
return obrimRetrieverJsonPath(provider, path.(string))
|
||||
}
|
||||
|
||||
return obrimRetrieverJsonResource(provider)
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonResource retrieves an entire resource.
|
||||
func obrimRetrieverJsonResource(provider any) map[string]any {
|
||||
data, success := obrimRetrieverJsonRetrieve(provider)
|
||||
|
||||
if !success {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
obrimRetrieverCodeFailedResourceNotFound,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
return obrimRetrieverBuildResponse(
|
||||
true,
|
||||
obrimRetrieverCodeSuccessResource,
|
||||
map[string]any{
|
||||
"data": data,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonPath retrieves a value using a dot-notation path.
|
||||
func obrimRetrieverJsonPath(provider any, path string) map[string]any {
|
||||
data, success := obrimRetrieverJsonRetrieve(provider)
|
||||
|
||||
if !success {
|
||||
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 values using field selection.
|
||||
func obrimRetrieverJsonFields(provider any, fieldsValue any) map[string]any {
|
||||
data, success := obrimRetrieverJsonRetrieve(provider)
|
||||
|
||||
if !success {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
obrimRetrieverCodeFailedResourceNotFound,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
fields, valid := obrimRetrieverJsonNormalizeFields(fieldsValue)
|
||||
|
||||
if !valid {
|
||||
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 retrieval configuration.
|
||||
func obrimRetrieverJsonValidate(config map[string]any) string {
|
||||
resourceValue, exists := config["resource"]
|
||||
|
||||
if !exists {
|
||||
return obrimRetrieverCodeFailedMissingConfig
|
||||
}
|
||||
|
||||
resource, valid := resourceValue.(string)
|
||||
|
||||
if !valid {
|
||||
return obrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
if strings.TrimSpace(resource) == "" {
|
||||
return obrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
_, pathExists := config["path"]
|
||||
_, fieldsExists := config["fields"]
|
||||
|
||||
if pathExists && fieldsExists {
|
||||
return obrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
|
||||
if pathExists {
|
||||
path, valid := config["path"].(string)
|
||||
|
||||
if !valid || strings.TrimSpace(path) == "" {
|
||||
return obrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
}
|
||||
|
||||
if fieldsExists {
|
||||
fields, valid := obrimRetrieverJsonNormalizeFields(config["fields"])
|
||||
|
||||
if !valid || len(fields) == 0 {
|
||||
return obrimRetrieverCodeFailedInvalidConfig
|
||||
}
|
||||
}
|
||||
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimRetrieverDb processes database retrieval requests.
|
||||
func obrimRetrieverDb(config map[string]any) map[string]any {
|
||||
return obrimRetrieverBuildResponse(
|
||||
false,
|
||||
obrimRetrieverCodeFailedNotImplemented,
|
||||
nil,
|
||||
)
|
||||
}
|
||||
|
||||
// obrimRetrieverDbValidate validates database retrieval configuration.
|
||||
func obrimRetrieverDbValidate(config map[string]any) string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonRetrieve retrieves resource data from a provider.
|
||||
func obrimRetrieverJsonRetrieve(provider any) (any, bool) {
|
||||
switch retrievalProvider := provider.(type) {
|
||||
case func() any:
|
||||
return retrievalProvider(), true
|
||||
|
||||
case func() map[string]any:
|
||||
return retrievalProvider(), true
|
||||
|
||||
case func() (any, bool):
|
||||
return retrievalProvider()
|
||||
|
||||
case map[string]any:
|
||||
return retrievalProvider, true
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonResolvePath resolves a dot-notation path.
|
||||
func obrimRetrieverJsonResolvePath(data any, path string) (any, bool) {
|
||||
current := data
|
||||
|
||||
for _, segment := range strings.Split(path, ".") {
|
||||
object, valid := current.(map[string]any)
|
||||
|
||||
if !valid {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
value, exists := object[segment]
|
||||
|
||||
if !exists {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
current = value
|
||||
}
|
||||
|
||||
return current, true
|
||||
}
|
||||
|
||||
// obrimRetrieverJsonNormalizeFields normalizes field selections.
|
||||
func obrimRetrieverJsonNormalizeFields(fieldsValue any) ([]string, bool) {
|
||||
switch fields := fieldsValue.(type) {
|
||||
case []string:
|
||||
return fields, true
|
||||
|
||||
case []any:
|
||||
result := make([]string, 0, len(fields))
|
||||
|
||||
for _, field := range fields {
|
||||
fieldString, valid := field.(string)
|
||||
|
||||
if !valid {
|
||||
return nil, false
|
||||
}
|
||||
|
||||
result = append(result, fieldString)
|
||||
}
|
||||
|
||||
return result, true
|
||||
|
||||
default:
|
||||
return nil, false
|
||||
}
|
||||
}
|
||||
@ -0,0 +1,124 @@
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Metadata
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Name:
|
||||
| - Status
|
||||
|
|
||||
| Purpose:
|
||||
| - Provide a framework-level CLI status utility that standardizes
|
||||
| terminal message presentation using semantic labels and visual
|
||||
| indicators to ensure consistent output across all softwares.
|
||||
|
|
||||
| Guideline:
|
||||
| - Call ObrimStatus() whenever a standardized terminal status message
|
||||
| must be emitted.
|
||||
| - Use only supported semantic labels: INFO, WARNING, SUCCESS, ERROR.
|
||||
| - Invalid, unsupported, or empty labels are automatically normalized
|
||||
| to INFO.
|
||||
| - Status operates through terminal output side-effects and does not
|
||||
| return any value.
|
||||
|
|
||||
| Example:
|
||||
| - ObrimStatus("INFO", "Application started.")
|
||||
| - ObrimStatus("SUCCESS", "Configuration loaded.")
|
||||
| - ObrimStatus("WARNING", "Configuration file not found.")
|
||||
| - ObrimStatus("ERROR", "Unable to establish connection.")
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
/*
|
||||
|--------------------------------------------------------------------------
|
||||
| Credit
|
||||
|--------------------------------------------------------------------------
|
||||
|
|
||||
| Contributor:
|
||||
| - Rajon Ahmed
|
||||
| - Blockonite
|
||||
|
|
||||
|--------------------------------------------------------------------------
|
||||
*/
|
||||
|
||||
package status
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
obrimStatusLabelInfo = "INFO"
|
||||
obrimStatusLabelWarning = "WARNING"
|
||||
obrimStatusLabelSuccess = "SUCCESS"
|
||||
obrimStatusLabelError = "ERROR"
|
||||
)
|
||||
|
||||
// obrimStatusIndicatorRegistry stores status indicators.
|
||||
var obrimStatusIndicatorRegistry = map[string]string{
|
||||
obrimStatusLabelInfo: "ℹ",
|
||||
obrimStatusLabelWarning: "⚠",
|
||||
obrimStatusLabelSuccess: "✓",
|
||||
obrimStatusLabelError: "✖",
|
||||
}
|
||||
|
||||
// ObrimStatus emits a standardized CLI status message.
|
||||
func ObrimStatus(label string, message string) {
|
||||
normalizedLabel := obrimStatusNormalizeLabel(label)
|
||||
formattedLabel := obrimStatusFormatLabel(normalizedLabel)
|
||||
formattedIndicator := obrimStatusFormatIndicator(normalizedLabel)
|
||||
formattedMessage := obrimStatusBuildMessage(
|
||||
formattedLabel,
|
||||
formattedIndicator,
|
||||
message,
|
||||
)
|
||||
|
||||
fmt.Println(formattedMessage)
|
||||
}
|
||||
|
||||
// obrimStatusNormalizeLabel normalizes and validates 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 labels.
|
||||
func obrimStatusFormatLabel(label string) string {
|
||||
return fmt.Sprintf("[%s]", label)
|
||||
}
|
||||
|
||||
// obrimStatusFormatIndicator generates visual status indicators.
|
||||
func obrimStatusFormatIndicator(label string) string {
|
||||
indicator, exists := obrimStatusIndicatorRegistry[label]
|
||||
if !exists {
|
||||
return obrimStatusIndicatorRegistry[obrimStatusLabelInfo]
|
||||
}
|
||||
|
||||
return indicator
|
||||
}
|
||||
|
||||
// obrimStatusBuildMessage constructs the final terminal message.
|
||||
func obrimStatusBuildMessage(
|
||||
label string,
|
||||
indicator string,
|
||||
message string,
|
||||
) string {
|
||||
return fmt.Sprintf("%s %s %s", indicator, label, message)
|
||||
}
|
||||
2
go.mod
2
go.mod
@ -1,3 +1,5 @@
|
||||
module go/module
|
||||
|
||||
go 1.26.4
|
||||
|
||||
require github.com/sqids/sqids-go v0.4.1
|
||||
|
||||
Loading…
Reference in New Issue
Block a user