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

393 lines
8.2 KiB
Go

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)
}