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

336 lines
8.0 KiB
Go

package progress
import (
"fmt"
"log"
"math"
"strings"
"sync"
)
const (
obrimProgressTypeCountable = "countable"
obrimProgressTypeUncountable = "uncountable"
obrimProgressStateStarted = "started"
obrimProgressStateRunning = "running"
obrimProgressStateCompleted = "completed"
obrimProgressStateCanceled = "canceled"
obrimProgressSuccessStarted = "SUCCESS_PROGRESS_STARTED"
obrimProgressSuccessRunning = "SUCCESS_PROGRESS_RUNNING"
obrimProgressSuccessCompleted = "SUCCESS_PROGRESS_COMPLETED"
obrimProgressSuccessCanceled = "SUCCESS_PROGRESS_CANCELED"
obrimProgressFailedMisconfigured = "FAILED_PROGRESS_MISCONFIGURED"
obrimProgressVisualWidth = 20
)
var (
// obrimProgressMutex protects progress state transitions from corruption.
obrimProgressMutex sync.Mutex
)
// ObrimProgressConfig defines progress configuration.
type ObrimProgressConfig struct {
State string `json:"state"`
Current int `json:"current,omitempty"`
Target int `json:"target,omitempty"`
Placeholder string `json:"placeholder,omitempty"`
}
// ObrimProgressResponse defines standardized utility responses.
type ObrimProgressResponse struct {
Status bool `json:"status"`
Code string `json:"code"`
Payload interface{} `json:"payload"`
}
// ObrimProgressCountablePayload defines countable progress payload.
type ObrimProgressCountablePayload struct {
State string `json:"state"`
Current int `json:"current"`
Target int `json:"target"`
Percentage int `json:"percentage"`
Message string `json:"message"`
Visual string `json:"visual"`
}
// ObrimProgressUncountablePayload defines uncountable progress payload.
type ObrimProgressUncountablePayload struct {
State string `json:"state"`
Placeholder string `json:"placeholder"`
Message string `json:"message"`
Visual string `json:"visual"`
}
// ObrimProgress manages lifecycle-aware countable and uncountable progress tracking operations.
func ObrimProgress(progressType string, config ObrimProgressConfig) ObrimProgressResponse {
obrimProgressMutex.Lock()
defer obrimProgressMutex.Unlock()
if err := obrimProgressValidate(progressType, config); err != nil {
return obrimProgressBuildResponse(false, obrimProgressFailedMisconfigured, nil)
}
stateCode := obrimProgressHandleState(config.State)
switch progressType {
case obrimProgressTypeCountable:
return obrimProgressCountable(config, stateCode)
case obrimProgressTypeUncountable:
return obrimProgressUncountable(config, stateCode)
default:
return obrimProgressBuildResponse(false, obrimProgressFailedMisconfigured, nil)
}
}
// obrimProgressValidate validates input parameters and configuration consistency.
func obrimProgressValidate(progressType string, config ObrimProgressConfig) error {
switch progressType {
case obrimProgressTypeCountable:
if config.Target <= 0 {
return fmt.Errorf("invalid target")
}
case obrimProgressTypeUncountable:
if strings.TrimSpace(config.Placeholder) == "" {
return fmt.Errorf("missing placeholder")
}
default:
return fmt.Errorf("invalid type")
}
switch config.State {
case obrimProgressStateStarted,
obrimProgressStateRunning,
obrimProgressStateCompleted,
obrimProgressStateCanceled:
return nil
default:
return fmt.Errorf("invalid state")
}
}
// obrimProgressHandleState processes lifecycle state transitions.
func obrimProgressHandleState(state string) string {
switch state {
case obrimProgressStateStarted:
log.Printf(`{"feature":"progress","event":"started"}`)
return obrimProgressSuccessStarted
case obrimProgressStateRunning:
log.Printf(`{"feature":"progress","event":"running"}`)
return obrimProgressSuccessRunning
case obrimProgressStateCompleted:
log.Printf(`{"feature":"progress","event":"completed"}`)
return obrimProgressSuccessCompleted
case obrimProgressStateCanceled:
log.Printf(`{"feature":"progress","event":"canceled"}`)
return obrimProgressSuccessCanceled
default:
return obrimProgressFailedMisconfigured
}
}
// obrimProgressCountable processes countable progress tracking workflows.
func obrimProgressCountable(
config ObrimProgressConfig,
code string,
) ObrimProgressResponse {
percentage := obrimProgressCalculatePercentage(
config.Current,
config.Target,
)
message := obrimProgressGenerateMessage(
obrimProgressTypeCountable,
config.State,
percentage,
"",
)
visual := obrimProgressGenerateVisual(
obrimProgressTypeCountable,
percentage,
"",
)
payload := ObrimProgressCountablePayload{
State: config.State,
Current: config.Current,
Target: config.Target,
Percentage: percentage,
Message: message,
Visual: visual,
}
return obrimProgressBuildResponse(true, code, payload)
}
// obrimProgressCalculatePercentage calculates and normalizes completion percentages.
func obrimProgressCalculatePercentage(current int, target int) int {
if current < 0 {
return 0
}
if target <= 0 {
return 0
}
percentage := int(
math.Round(
(float64(current) / float64(target)) * 100,
),
)
if percentage > 100 {
return 100
}
if percentage < 0 {
return 0
}
return percentage
}
// obrimProgressUncountable processes uncountable progress tracking workflows.
func obrimProgressUncountable(
config ObrimProgressConfig,
code string,
) ObrimProgressResponse {
placeholder := obrimProgressResolvePlaceholder(
config.Placeholder,
)
message := obrimProgressGenerateMessage(
obrimProgressTypeUncountable,
config.State,
0,
placeholder,
)
visual := obrimProgressGenerateVisual(
obrimProgressTypeUncountable,
0,
placeholder,
)
payload := ObrimProgressUncountablePayload{
State: config.State,
Placeholder: placeholder,
Message: message,
Visual: visual,
}
return obrimProgressBuildResponse(true, code, payload)
}
// obrimProgressResolvePlaceholder validates and prepares placeholder activity messages.
func obrimProgressResolvePlaceholder(placeholder string) string {
return strings.TrimSpace(placeholder)
}
// obrimProgressGenerateMessage generates human-readable progress messages.
func obrimProgressGenerateMessage(
progressType string,
state string,
percentage int,
placeholder string,
) string {
switch progressType {
case obrimProgressTypeCountable:
switch state {
case obrimProgressStateStarted:
return fmt.Sprintf("Progress started (%d%% complete).", percentage)
case obrimProgressStateRunning:
return fmt.Sprintf("Progress running (%d%% complete).", percentage)
case obrimProgressStateCompleted:
return "Progress completed successfully."
case obrimProgressStateCanceled:
return "Progress canceled."
}
case obrimProgressTypeUncountable:
switch state {
case obrimProgressStateStarted:
return fmt.Sprintf("Activity started: %s", placeholder)
case obrimProgressStateRunning:
return fmt.Sprintf("Activity running: %s", placeholder)
case obrimProgressStateCompleted:
return fmt.Sprintf("Activity completed: %s", placeholder)
case obrimProgressStateCanceled:
return fmt.Sprintf("Activity canceled: %s", placeholder)
}
}
return ""
}
// obrimProgressGenerateVisual generates CLI-friendly visual progress representations.
func obrimProgressGenerateVisual(
progressType string,
percentage int,
placeholder string,
) string {
switch progressType {
case obrimProgressTypeCountable:
filled := int(
math.Round(
(float64(percentage) / 100.0) * obrimProgressVisualWidth,
),
)
if filled > obrimProgressVisualWidth {
filled = obrimProgressVisualWidth
}
if filled < 0 {
filled = 0
}
return fmt.Sprintf(
"[%s%s] %d%%",
strings.Repeat("=", filled),
strings.Repeat(" ", obrimProgressVisualWidth-filled),
percentage,
)
case obrimProgressTypeUncountable:
return fmt.Sprintf("[...] %s", placeholder)
}
return ""
}
// obrimProgressBuildResponse constructs standardized utility response payloads.
func obrimProgressBuildResponse(
status bool,
code string,
payload interface{},
) ObrimProgressResponse {
if !status {
payload = nil
}
return ObrimProgressResponse{
Status: status,
Code: code,
Payload: payload,
}
}