package domain import ( "errors" "fmt" ) var ErrInvalidStatusFacts = errors.New("invalid app status facts") // AppStatusFacts are IO-free observations used to derive one visible state. type AppStatusFacts struct { Operation AppStatus Running bool RecoveryPending bool Incompatible bool InstalledVersion string CatalogVersion string } // ResolveAppStatus derives the single user-visible state from local and // background-operation facts. func ResolveAppStatus(facts AppStatusFacts) (AppStatus, error) { if facts.RecoveryPending { return StatusRollbackPending, nil } if facts.Operation != "" { if !validOperationStatus(facts.Operation) { return "", fmt.Errorf( "%w: operation %q", ErrInvalidStatusFacts, facts.Operation, ) } return facts.Operation, nil } if facts.Running { return StatusRunning, nil } if facts.Incompatible { return StatusIncompatible, nil } if facts.InstalledVersion == "" { return StatusNotInstalled, nil } if _, err := ParseSemVer(facts.InstalledVersion); err != nil { return "", fmt.Errorf("%w: installed version: %v", ErrInvalidStatusFacts, err) } if facts.CatalogVersion == "" { return StatusInstalled, nil } comparison, err := CompareSemVer(facts.InstalledVersion, facts.CatalogVersion) if err != nil { return "", fmt.Errorf("%w: catalog version: %v", ErrInvalidStatusFacts, err) } if comparison < 0 { return StatusUpdateAvailable, nil } return StatusInstalled, nil } func validOperationStatus(status AppStatus) bool { switch status { case StatusQueued, StatusDownloading, StatusVerifying, StatusExtracting, StatusInstalling, StatusFailed: return true default: return false } }