obrimbaseapi/essential/visible/service/helper/progress/progress.go

444 lines
10 KiB
Go

/*
|--------------------------------------------------------------------------
| Manifest
|--------------------------------------------------------------------------
|
| Name:
| - Progress
|
| Purpose:
| - Manage, monitor, and report task progress through standardized
| lifecycle-aware progress tracking.
|
| Guideline:
| - Use countable progress when current and target values are available.
| - Use uncountable progress when only activity status is available.
| - Always provide a valid lifecycle state.
| - Use standardized response structures for all executions.
|
| Example:
| - ObrimProgress("countable", map[string]any{
| "state":"running",
| "current":25,
| "target":100,
| })
|
| - ObrimProgress("uncountable", map[string]any{
| "state":"running",
| "placeholder":"Synchronizing resources",
| })
|
|--------------------------------------------------------------------------
*/
/*
|--------------------------------------------------------------------------
| Credit
|--------------------------------------------------------------------------
|
| Contributor:
| - Rajon Ahmed
| - Scionite
|
|--------------------------------------------------------------------------
*/
package progress
import (
"fmt"
"math"
"strings"
"sync"
"go/module/essential/visible/service/helper/log"
"go/module/essential/visible/service/helper/status"
)
const (
obrimProgressTypeCountable = "countable"
obrimProgressTypeUncountable = "uncountable"
obrimProgressStateStarted = "started"
obrimProgressStateRunning = "running"
obrimProgressStateCompleted = "completed"
obrimProgressStateCanceled = "canceled"
obrimProgressVisualWidth = 20
)
var (
// obrimProgressStateMutex protects lifecycle transitions.
obrimProgressStateMutex sync.Mutex
)
// 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,
"FAILED_PROGRESS_MISCONFIGURED",
nil,
)
}
// lifecycleState stores validated lifecycle state.
lifecycleState := strings.ToLower(fmt.Sprintf("%v", config["state"]))
if !obrimProgressHandleState(lifecycleState) {
return obrimProgressBuildResponse(
false,
"FAILED_PROGRESS_MISCONFIGURED",
nil,
)
}
switch progressType {
case obrimProgressTypeCountable:
return obrimProgressCountable(config)
case obrimProgressTypeUncountable:
return obrimProgressUncountable(config)
default:
return obrimProgressBuildResponse(
false,
"FAILED_PROGRESS_MISCONFIGURED",
nil,
)
}
}
// obrimProgressValidate validates input parameters and configuration consistency.
func obrimProgressValidate(progressType string, config map[string]any) bool {
if strings.TrimSpace(progressType) == "" {
log.ObrimLog("ERROR", "Progress type is missing.")
status.ObrimStatus("ERROR", "Progress type is required.")
return false
}
if config == nil {
log.ObrimLog("ERROR", "Progress configuration is missing.")
status.ObrimStatus("ERROR", "Progress configuration is required.")
return false
}
// lifecycleState stores lifecycle state.
lifecycleState := strings.ToLower(fmt.Sprintf("%v", config["state"]))
switch lifecycleState {
case obrimProgressStateStarted,
obrimProgressStateRunning,
obrimProgressStateCompleted,
obrimProgressStateCanceled:
default:
log.ObrimLog("ERROR", "Invalid progress lifecycle state.")
status.ObrimStatus("ERROR", "Invalid progress lifecycle state.")
return false
}
switch progressType {
case obrimProgressTypeCountable:
_, currentExists := config["current"]
_, targetExists := config["target"]
if !currentExists || !targetExists {
return false
}
// targetValue stores target progress value.
targetValue, ok := obrimProgressConvertToInt(config["target"])
if !ok || targetValue <= 0 {
return false
}
case obrimProgressTypeUncountable:
placeholder, ok := config["placeholder"].(string)
if !ok || strings.TrimSpace(placeholder) == "" {
return false
}
default:
return false
}
return true
}
// obrimProgressHandleState processes lifecycle state transitions.
func obrimProgressHandleState(state string) bool {
switch state {
case obrimProgressStateStarted:
log.ObrimLog("INIT", "Progress tracking started.")
status.ObrimStatus("INFO", "Progress started.")
return true
case obrimProgressStateRunning:
log.ObrimLog("PROGRESS", "Progress tracking updated.")
status.ObrimStatus("INFO", "Progress running.")
return true
case obrimProgressStateCompleted:
log.ObrimLog("SUCCESS", "Progress tracking completed.")
status.ObrimStatus("SUCCESS", "Progress completed.")
return true
case obrimProgressStateCanceled:
log.ObrimLog("WARNING", "Progress tracking canceled.")
status.ObrimStatus("WARNING", "Progress canceled.")
return true
}
return false
}
// obrimProgressCountable processes countable progress tracking workflows.
func obrimProgressCountable(config map[string]any) map[string]any {
// lifecycleState stores lifecycle state.
lifecycleState := strings.ToLower(fmt.Sprintf("%v", config["state"]))
// currentValue stores current progress value.
currentValue, _ := obrimProgressConvertToInt(config["current"])
// targetValue stores target progress value.
targetValue, _ := obrimProgressConvertToInt(config["target"])
// percentage stores normalized completion percentage.
percentage := obrimProgressCalculatePercentage(
currentValue,
targetValue,
)
// progressMessage stores human readable progress message.
progressMessage := obrimProgressGenerateMessage(
obrimProgressTypeCountable,
lifecycleState,
percentage,
"",
)
// progressVisual stores visual progress representation.
progressVisual := obrimProgressGenerateVisual(
obrimProgressTypeCountable,
percentage,
"",
)
payload := map[string]any{
"state": lifecycleState,
"current": currentValue,
"target": targetValue,
"percentage": percentage,
"message": progressMessage,
"visual": progressVisual,
}
return obrimProgressBuildResponse(
true,
"SUCCESS_PROGRESS_TRACKED",
payload,
)
}
// obrimProgressCalculatePercentage calculates and normalizes completion percentages.
func obrimProgressCalculatePercentage(current int, target int) int {
if target <= 0 {
return 0
}
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) map[string]any {
// lifecycleState stores lifecycle state.
lifecycleState := strings.ToLower(fmt.Sprintf("%v", config["state"]))
// placeholder stores resolved placeholder text.
placeholder := obrimProgressResolvePlaceholder(config)
// progressMessage stores human readable progress message.
progressMessage := obrimProgressGenerateMessage(
obrimProgressTypeUncountable,
lifecycleState,
0,
placeholder,
)
// progressVisual stores visual progress representation.
progressVisual := obrimProgressGenerateVisual(
obrimProgressTypeUncountable,
0,
placeholder,
)
payload := map[string]any{
"state": lifecycleState,
"placeholder": placeholder,
"message": progressMessage,
"visual": progressVisual,
}
return obrimProgressBuildResponse(
true,
"SUCCESS_PROGRESS_TRACKED",
payload,
)
}
// obrimProgressResolvePlaceholder validates and prepares placeholder activity messages.
func obrimProgressResolvePlaceholder(config map[string]any) string {
placeholder, ok := config["placeholder"].(string)
if !ok {
return ""
}
return strings.TrimSpace(placeholder)
}
// obrimProgressGenerateMessage generates human-readable progress messages.
func obrimProgressGenerateMessage(
progressType string,
state string,
percentage int,
placeholder string,
) string {
if progressType == obrimProgressTypeCountable {
switch state {
case obrimProgressStateStarted:
return fmt.Sprintf("Progress started (%d%%).", percentage)
case obrimProgressStateRunning:
return fmt.Sprintf("Progress running (%d%% complete).", percentage)
case obrimProgressStateCompleted:
return "Progress completed successfully."
case obrimProgressStateCanceled:
return "Progress canceled."
}
}
switch state {
case obrimProgressStateStarted:
return fmt.Sprintf("Activity started: %s.", placeholder)
case obrimProgressStateRunning:
return fmt.Sprintf("Activity running: %s.", placeholder)
case obrimProgressStateCompleted:
return fmt.Sprintf("Activity completed: %s.", placeholder)
case obrimProgressStateCanceled:
return fmt.Sprintf("Activity canceled: %s.", placeholder)
}
return ""
}
// obrimProgressGenerateVisual generates CLI-friendly visual progress representations.
func obrimProgressGenerateVisual(
progressType string,
percentage int,
placeholder string,
) string {
if progressType == obrimProgressTypeCountable {
filled := int(
math.Round(
(float64(percentage) / 100) * obrimProgressVisualWidth,
),
)
if filled > obrimProgressVisualWidth {
filled = obrimProgressVisualWidth
}
if filled < 0 {
filled = 0
}
return fmt.Sprintf(
"[%s%s] %d%%",
strings.Repeat("#", filled),
strings.Repeat("-", obrimProgressVisualWidth-filled),
percentage,
)
}
return fmt.Sprintf("[~] %s", placeholder)
}
// obrimProgressBuildResponse constructs standardized utility response payloads.
func obrimProgressBuildResponse(
success bool,
code string,
payload map[string]any,
) map[string]any {
if !success {
return map[string]any{
"status": false,
"code": code,
"payload": nil,
}
}
return map[string]any{
"status": true,
"code": code,
"payload": payload,
}
}
// obrimProgressConvertToInt safely converts supported numeric values to int.
func obrimProgressConvertToInt(value any) (int, bool) {
switch v := value.(type) {
case int:
return v, true
case int8:
return int(v), true
case int16:
return int(v), true
case int32:
return int(v), true
case int64:
return int(v), true
case uint:
return int(v), true
case uint8:
return int(v), true
case uint16:
return int(v), true
case uint32:
return int(v), true
case uint64:
return int(v), true
case float32:
return int(v), true
case float64:
return int(v), true
default:
return 0, false
}
}