Update from /github.com/vbauerster/mpb/v7 to /v8

Also update to c/image after https://github.com/containers/image/pull/1821 ,
so that we don't ship two versions of the package simultaneously.

[NO NEW TESTS NEEDED]

Signed-off-by: Miloslav Trmač <mitr@redhat.com>
This commit is contained in:
Miloslav Trmač 2023-02-02 18:35:24 +01:00
parent e64508378f
commit be47eeb85c
58 changed files with 1078 additions and 791 deletions

6
go.mod
View file

@ -14,7 +14,7 @@ require (
github.com/containers/buildah v1.29.0
github.com/containers/common v0.51.0
github.com/containers/conmon v2.0.20+incompatible
github.com/containers/image/v5 v5.24.0
github.com/containers/image/v5 v5.24.1-0.20230202144111-a49c94a010be
github.com/containers/ocicrypt v1.1.7
github.com/containers/psgo v1.8.0
github.com/containers/storage v1.45.3
@ -57,7 +57,7 @@ require (
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/ulikunitz/xz v0.5.11
github.com/vbauerster/mpb/v7 v7.5.3
github.com/vbauerster/mpb/v8 v8.1.4
github.com/vishvananda/netlink v1.2.1-beta.2
go.etcd.io/bbolt v1.3.7
golang.org/x/net v0.5.0
@ -153,7 +153,7 @@ require (
github.com/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
github.com/sylabs/sif/v2 v2.9.0 // indirect
github.com/tchap/go-patricia v2.3.0+incompatible // indirect
github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5 // indirect
github.com/theupdateframework/go-tuf v0.5.2 // indirect
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // indirect

14
go.sum
View file

@ -272,8 +272,8 @@ github.com/containers/common v0.51.0 h1:Ax4YHNTG8cEPHZJcMYRoP7sfBgOISceeyOvmZzmS
github.com/containers/common v0.51.0/go.mod h1:3W2WIdalgQfrsX/T5tjX+6CxgT3ThJVN2G9sNuFjuCM=
github.com/containers/conmon v2.0.20+incompatible h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I=
github.com/containers/image/v5 v5.24.0 h1:2Pu8ztTntqNxteVN15bORCQnM8rfnbYuyKwUiiKUBuc=
github.com/containers/image/v5 v5.24.0/go.mod h1:oss5F6ssGQz8ZtC79oY+fuzYA3m3zBek9tq9gmhuvHc=
github.com/containers/image/v5 v5.24.1-0.20230202144111-a49c94a010be h1:Bcsn9ohcVpCM9D2kZvIVMrO0QmmP87jhVZh/r9WrQ18=
github.com/containers/image/v5 v5.24.1-0.20230202144111-a49c94a010be/go.mod h1:7Aonfvk5Wz0DLP40xgVSOGvygK3JXvDLxgotyGp4/KA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY=
github.com/containers/ocicrypt v1.0.1/go.mod h1:MeJDzk1RJHv89LjsH0Sp5KTY3ZYkjXO/C+bKAeWFIrc=
@ -756,7 +756,6 @@ github.com/mattn/go-isatty v0.0.4/go.mod h1:M+lRXTBqGeGNdLjl/ufCoiOlB5xdOkqRJdNx
github.com/mattn/go-isatty v0.0.16 h1:bq3VjFmv/sOjHtdEhmkEV4x1AJtvUvOJ2PFAZ5+peKQ=
github.com/mattn/go-runewidth v0.0.2/go.mod h1:LwmH8dsx7+W8Uxz3IHJYH5QSwggIsqBzpuz5H//U1FU=
github.com/mattn/go-runewidth v0.0.9/go.mod h1:H031xJmbD/WCDINGzjvQ9THkh0rPKHF+m2gUSrubnMI=
github.com/mattn/go-runewidth v0.0.13/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-runewidth v0.0.14 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
github.com/mattn/go-shellwords v1.0.3/go.mod h1:3xCvwCdWdlDJUrvuMn7Wuy9eWs4pE8vqg+NOMyg4B2o=
@ -1045,8 +1044,8 @@ github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635/go.mod h1:hkRG
github.com/tchap/go-patricia v2.2.6+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/tchap/go-patricia v2.3.0+incompatible h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs=
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I=
github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5 h1:s+Yvt6bzRwHljSE7j6DLBDcfpZEdBhrvLgOUmd8f7ZM=
github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5/go.mod h1:Le8NAjvDJK1vmLgpVYr4AR1Tqam/b/mTdQyTy37UJDA=
github.com/theupdateframework/go-tuf v0.5.2 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=
github.com/theupdateframework/go-tuf v0.5.2/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 h1:e/5i7d4oYZ+C1wj2THlRK+oAhjeS/TRQwMfkIuet3w0=
@ -1068,8 +1067,8 @@ github.com/urfave/cli v1.22.4/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtX
github.com/urfave/cli v1.22.9/go.mod h1:Gos4lmkARVdJ6EkW0WaNv/tZAAMe9V7XWyB60NtXRu0=
github.com/vbatts/tar-split v0.11.2 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI=
github.com/vbauerster/mpb/v7 v7.5.3 h1:BkGfmb6nMrrBQDFECR/Q7RkKCw7ylMetCb4079CGs4w=
github.com/vbauerster/mpb/v7 v7.5.3/go.mod h1:i+h4QY6lmLvBNK2ah1fSreiw3ajskRlBp9AhY/PnuOE=
github.com/vbauerster/mpb/v8 v8.1.4 h1:MOcLTIbbAA892wVjRiuFHa1nRlNvifQMDVh12Bq/xIs=
github.com/vbauerster/mpb/v8 v8.1.4/go.mod h1:2fRME8lCLU9gwJwghZb1bO9A3Plc8KPeQ/ayGj+Ek4I=
github.com/vishvananda/netlink v0.0.0-20181108222139-023a6dafdcdf/go.mod h1:+SR5DhBJrl6ZM7CoCKvpw5BKroDKQ+PJqOg65H/2ktk=
github.com/vishvananda/netlink v1.1.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho=
@ -1402,7 +1401,6 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
golang.org/x/sys v0.0.0-20220817070843-5a390386f1f2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220823224334-20c2bfdbfe24/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220908164124-27713097b956/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220909162455-aba9fc2a8ff2/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=

View file

@ -20,8 +20,8 @@ import (
"github.com/containers/storage/pkg/archive"
"github.com/sirupsen/logrus"
"github.com/ulikunitz/xz"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
)
// GenericDownload is used when a user provides a URL

View file

@ -31,7 +31,7 @@ import (
digest "github.com/opencontainers/go-digest"
imgspecv1 "github.com/opencontainers/image-spec/specs-go/v1"
"github.com/sirupsen/logrus"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v8"
"golang.org/x/sync/semaphore"
"golang.org/x/term"
)

View file

@ -7,8 +7,8 @@ import (
"github.com/containers/image/v5/internal/private"
"github.com/containers/image/v5/types"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
)
// newProgressPool creates a *mpb.Progress.

View file

@ -13,9 +13,9 @@ import (
"github.com/containers/image/v5/internal/rootless"
"github.com/containers/image/v5/types"
"github.com/containers/storage/pkg/homedir"
"github.com/ghodss/yaml"
"github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
)
// systemRegistriesDirPath is the path to registries.d, used for locating lookaside Docker signature storage.
@ -39,18 +39,18 @@ var defaultDockerDir = "/var/lib/containers/sigstore"
// registryConfiguration is one of the files in registriesDirPath configuring lookaside locations, or the result of merging them all.
// NOTE: Keep this in sync with docs/registries.d.md!
type registryConfiguration struct {
DefaultDocker *registryNamespace `json:"default-docker"`
DefaultDocker *registryNamespace `yaml:"default-docker"`
// The key is a namespace, using fully-expanded Docker reference format or parent namespaces (per dockerReference.PolicyConfiguration*),
Docker map[string]registryNamespace `json:"docker"`
Docker map[string]registryNamespace `yaml:"docker"`
}
// registryNamespace defines lookaside locations for a single namespace.
type registryNamespace struct {
Lookaside string `json:"lookaside"` // For reading, and if LookasideStaging is not present, for writing.
LookasideStaging string `json:"lookaside-staging"` // For writing only.
SigStore string `json:"sigstore"` // For compatibility, deprecated in favor of Lookaside.
SigStoreStaging string `json:"sigstore-staging"` // For compatibility, deprecated in favor of LookasideStaging.
UseSigstoreAttachments *bool `json:"use-sigstore-attachments,omitempty"`
Lookaside string `yaml:"lookaside"` // For reading, and if LookasideStaging is not present, for writing.
LookasideStaging string `yaml:"lookaside-staging"` // For writing only.
SigStore string `yaml:"sigstore"` // For compatibility, deprecated in favor of Lookaside.
SigStoreStaging string `yaml:"sigstore-staging"` // For compatibility, deprecated in favor of LookasideStaging.
UseSigstoreAttachments *bool `yaml:"use-sigstore-attachments,omitempty"`
}
// lookasideStorageBase is an "opaque" type representing a lookaside Docker signature storage.

View file

@ -2,6 +2,10 @@ package manifest
import "fmt"
// FIXME: This is a duplicate of c/image/manifestDockerV2Schema2ConfigMediaType.
// Deduplicate that, depending on outcome of https://github.com/containers/image/pull/1791 .
const dockerV2Schema2ConfigMediaType = "application/vnd.docker.container.image.v1+json"
// NonImageArtifactError (detected via errors.As) is used when asking for an image-specific operation
// on an object which is not a “container image” in the standard sense (e.g. an OCI artifact)
//
@ -28,5 +32,9 @@ func NewNonImageArtifactError(mimeType string) error {
}
func (e NonImageArtifactError) Error() string {
// Special-case these invalid mixed images, which show up from time to time:
if e.mimeType == dockerV2Schema2ConfigMediaType {
return fmt.Sprintf("invalid mixed OCI image with Docker v2s2 config (%q)", e.mimeType)
}
return fmt.Sprintf("unsupported image-specific operation on artifact with type %q", e.mimeType)
}

View file

@ -3,7 +3,7 @@ package openshift
import (
"crypto/tls"
"crypto/x509"
"encoding/json"
"encoding/base64"
"errors"
"fmt"
"net"
@ -17,10 +17,10 @@ import (
"time"
"github.com/containers/storage/pkg/homedir"
"github.com/ghodss/yaml"
"github.com/imdario/mergo"
"github.com/sirupsen/logrus"
"golang.org/x/net/http2"
"gopkg.in/yaml.v3"
)
// restTLSClientConfig is a modified copy of k8s.io/kubernetes/pkg/client/restclient.TLSClientConfig.
@ -672,11 +672,7 @@ func load(data []byte) (*clientcmdConfig, error) {
return config, nil
}
// Note: This does absolutely no kind/version checking or conversions.
data, err := yaml.YAMLToJSON(data)
if err != nil {
return nil, err
}
if err := json.Unmarshal(data, config); err != nil {
if err := yaml.Unmarshal(data, config); err != nil {
return nil, err
}
return config, nil
@ -1057,20 +1053,20 @@ func (c *restConfig) HasCertAuth() bool {
// IMPORTANT if you add fields to this struct, please update IsConfigEmpty()
type clientcmdConfig struct {
// Clusters is a map of referenceable names to cluster configs
Clusters clustersMap `json:"clusters"`
Clusters clustersMap `yaml:"clusters"`
// AuthInfos is a map of referenceable names to user configs
AuthInfos authInfosMap `json:"users"`
AuthInfos authInfosMap `yaml:"users"`
// Contexts is a map of referenceable names to context configs
Contexts contextsMap `json:"contexts"`
Contexts contextsMap `yaml:"contexts"`
// CurrentContext is the name of the context that you would like to use by default
CurrentContext string `json:"current-context"`
CurrentContext string `yaml:"current-context"`
}
type clustersMap map[string]*clientcmdCluster
func (m *clustersMap) UnmarshalJSON(data []byte) error {
func (m *clustersMap) UnmarshalYAML(value *yaml.Node) error {
var a []v1NamedCluster
if err := json.Unmarshal(data, &a); err != nil {
if err := value.Decode(&a); err != nil {
return err
}
for _, e := range a {
@ -1082,9 +1078,9 @@ func (m *clustersMap) UnmarshalJSON(data []byte) error {
type authInfosMap map[string]*clientcmdAuthInfo
func (m *authInfosMap) UnmarshalJSON(data []byte) error {
func (m *authInfosMap) UnmarshalYAML(value *yaml.Node) error {
var a []v1NamedAuthInfo
if err := json.Unmarshal(data, &a); err != nil {
if err := value.Decode(&a); err != nil {
return err
}
for _, e := range a {
@ -1096,9 +1092,9 @@ func (m *authInfosMap) UnmarshalJSON(data []byte) error {
type contextsMap map[string]*clientcmdContext
func (m *contextsMap) UnmarshalJSON(data []byte) error {
func (m *contextsMap) UnmarshalYAML(value *yaml.Node) error {
var a []v1NamedContext
if err := json.Unmarshal(data, &a); err != nil {
if err := value.Decode(&a); err != nil {
return err
}
for _, e := range a {
@ -1118,19 +1114,32 @@ func clientcmdNewConfig() *clientcmdConfig {
}
}
// yamlBinaryAsBase64String is a []byte that can be stored in yaml as a !!str, not a !!binary
type yamlBinaryAsBase64String []byte
func (bin *yamlBinaryAsBase64String) UnmarshalText(text []byte) error {
res := make([]byte, base64.StdEncoding.DecodedLen(len(text)))
n, err := base64.StdEncoding.Decode(res, text)
if err != nil {
return err
}
*bin = res[:n]
return nil
}
// clientcmdCluster is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.Cluster.
// Cluster contains information about how to communicate with a kubernetes cluster
type clientcmdCluster struct {
// LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized.
LocationOfOrigin string
// Server is the address of the kubernetes cluster (https://hostname:port).
Server string `json:"server"`
Server string `yaml:"server"`
// InsecureSkipTLSVerify skips the validity check for the server's certificate. This will make your HTTPS connections insecure.
InsecureSkipTLSVerify bool `json:"insecure-skip-tls-verify,omitempty"`
InsecureSkipTLSVerify bool `yaml:"insecure-skip-tls-verify,omitempty"`
// CertificateAuthority is the path to a cert file for the certificate authority.
CertificateAuthority string `json:"certificate-authority,omitempty"`
CertificateAuthority string `yaml:"certificate-authority,omitempty"`
// CertificateAuthorityData contains PEM-encoded certificate authority certificates. Overrides CertificateAuthority
CertificateAuthorityData []byte `json:"certificate-authority-data,omitempty"`
CertificateAuthorityData yamlBinaryAsBase64String `yaml:"certificate-authority-data,omitempty"`
}
// clientcmdAuthInfo is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.AuthInfo.
@ -1139,19 +1148,19 @@ type clientcmdAuthInfo struct {
// LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized.
LocationOfOrigin string
// ClientCertificate is the path to a client cert file for TLS.
ClientCertificate string `json:"client-certificate,omitempty"`
ClientCertificate string `yaml:"client-certificate,omitempty"`
// ClientCertificateData contains PEM-encoded data from a client cert file for TLS. Overrides ClientCertificate
ClientCertificateData []byte `json:"client-certificate-data,omitempty"`
ClientCertificateData yamlBinaryAsBase64String `yaml:"client-certificate-data,omitempty"`
// ClientKey is the path to a client key file for TLS.
ClientKey string `json:"client-key,omitempty"`
ClientKey string `yaml:"client-key,omitempty"`
// ClientKeyData contains PEM-encoded data from a client key file for TLS. Overrides ClientKey
ClientKeyData []byte `json:"client-key-data,omitempty"`
ClientKeyData yamlBinaryAsBase64String `yaml:"client-key-data,omitempty"`
// Token is the bearer token for authentication to the kubernetes cluster.
Token string `json:"token,omitempty"`
Token string `yaml:"token,omitempty"`
// Username is the username for basic authentication to the kubernetes cluster.
Username string `json:"username,omitempty"`
Username string `yaml:"username,omitempty"`
// Password is the password for basic authentication to the kubernetes cluster.
Password string `json:"password,omitempty"`
Password string `yaml:"password,omitempty"`
}
// clientcmdContext is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.Context.
@ -1160,36 +1169,36 @@ type clientcmdContext struct {
// LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized.
LocationOfOrigin string
// Cluster is the name of the cluster for this context
Cluster string `json:"cluster"`
Cluster string `yaml:"cluster"`
// AuthInfo is the name of the authInfo for this context
AuthInfo string `json:"user"`
AuthInfo string `yaml:"user"`
// Namespace is the default namespace to use on unspecified requests
Namespace string `json:"namespace,omitempty"`
Namespace string `yaml:"namespace,omitempty"`
}
// v1NamedCluster is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.v1.NamedCluster.
// NamedCluster relates nicknames to cluster information
type v1NamedCluster struct {
// Name is the nickname for this Cluster
Name string `json:"name"`
Name string `yaml:"name"`
// Cluster holds the cluster information
Cluster clientcmdCluster `json:"cluster"`
Cluster clientcmdCluster `yaml:"cluster"`
}
// v1NamedContext is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.v1.NamedContext.
// NamedContext relates nicknames to context information
type v1NamedContext struct {
// Name is the nickname for this Context
Name string `json:"name"`
Name string `yaml:"name"`
// Context holds the context information
Context clientcmdContext `json:"context"`
Context clientcmdContext `yaml:"context"`
}
// v1NamedAuthInfo is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.v1.NamedAuthInfo.
// NamedAuthInfo relates nicknames to auth information
type v1NamedAuthInfo struct {
// Name is the nickname for this AuthInfo
Name string `json:"name"`
Name string `yaml:"name"`
// AuthInfo holds the auth information
AuthInfo clientcmdAuthInfo `json:"user"`
AuthInfo clientcmdAuthInfo `yaml:"user"`
}

View file

@ -8,10 +8,10 @@ const (
// VersionMinor is for functionality in a backwards-compatible manner
VersionMinor = 24
// VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0
VersionPatch = 1
// VersionDev indicates development branch. Releases will be empty string.
VersionDev = ""
VersionDev = "-dev"
)
// Version is the specification version that the package types support.

View file

@ -1,91 +0,0 @@
package cwriter
import (
"bytes"
"errors"
"io"
"os"
"strconv"
)
// ErrNotTTY not a TeleTYpewriter error.
var ErrNotTTY = errors.New("not a terminal")
// https://github.com/dylanaraps/pure-sh-bible#cursor-movement
const (
escOpen = "\x1b["
cuuAndEd = "A\x1b[J"
)
// Writer is a buffered the writer that updates the terminal. The
// contents of writer will be flushed when Flush is called.
type Writer struct {
out io.Writer
buf bytes.Buffer
lines int // how much lines to clear before flushing new ones
fd int
terminal bool
termSize func(int) (int, int, error)
}
// New returns a new Writer with defaults.
func New(out io.Writer) *Writer {
w := &Writer{
out: out,
termSize: func(_ int) (int, int, error) {
return -1, -1, ErrNotTTY
},
}
if f, ok := out.(*os.File); ok {
w.fd = int(f.Fd())
if IsTerminal(w.fd) {
w.terminal = true
w.termSize = func(fd int) (int, int, error) {
return GetSize(fd)
}
}
}
return w
}
// Flush flushes the underlying buffer.
func (w *Writer) Flush(lines int) (err error) {
// some terminals interpret 'cursor up 0' as 'cursor up 1'
if w.lines > 0 {
err = w.clearLines()
if err != nil {
return
}
}
w.lines = lines
_, err = w.buf.WriteTo(w.out)
return
}
// Write appends the contents of p to the underlying buffer.
func (w *Writer) Write(p []byte) (n int, err error) {
return w.buf.Write(p)
}
// WriteString writes string to the underlying buffer.
func (w *Writer) WriteString(s string) (n int, err error) {
return w.buf.WriteString(s)
}
// ReadFrom reads from the provided io.Reader and writes to the
// underlying buffer.
func (w *Writer) ReadFrom(r io.Reader) (n int64, err error) {
return w.buf.ReadFrom(r)
}
// GetTermSize returns WxH of underlying terminal.
func (w *Writer) GetTermSize() (width, height int, err error) {
return w.termSize(w.fd)
}
func (w *Writer) ansiCuuAndEd() error {
buf := make([]byte, 8)
buf = strconv.AppendInt(buf[:copy(buf, escOpen)], int64(w.lines), 10)
_, err := w.out.Write(append(buf, cuuAndEd...))
return err
}

View file

@ -1,26 +0,0 @@
// +build !windows
package cwriter
import (
"golang.org/x/sys/unix"
)
func (w *Writer) clearLines() error {
return w.ansiCuuAndEd()
}
// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
if err != nil {
return -1, -1, err
}
return int(ws.Col), int(ws.Row), nil
}
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil
}

View file

@ -1,10 +0,0 @@
package decor
import "io"
func mustWriteString(w io.Writer, s string) {
_, err := io.WriteString(w, s)
if err != nil {
panic(err)
}
}

View file

@ -1,79 +0,0 @@
package mpb
import (
"io"
"time"
)
type proxyReader struct {
io.ReadCloser
bar *Bar
}
func (x proxyReader) Read(p []byte) (int, error) {
n, err := x.ReadCloser.Read(p)
x.bar.IncrBy(n)
return n, err
}
type proxyWriterTo struct {
proxyReader
wt io.WriterTo
}
func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
n, err := x.wt.WriteTo(w)
x.bar.IncrInt64(n)
return n, err
}
type ewmaProxyReader struct {
proxyReader
}
func (x ewmaProxyReader) Read(p []byte) (int, error) {
start := time.Now()
n, err := x.proxyReader.Read(p)
if n > 0 {
x.bar.DecoratorEwmaUpdate(time.Since(start))
}
return n, err
}
type ewmaProxyWriterTo struct {
ewmaProxyReader
wt proxyWriterTo
}
func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
start := time.Now()
n, err := x.wt.WriteTo(w)
if n > 0 {
x.bar.DecoratorEwmaUpdate(time.Since(start))
}
return n, err
}
func (b *Bar) newProxyReader(r io.Reader) (rc io.ReadCloser) {
pr := proxyReader{toReadCloser(r), b}
if wt, ok := r.(io.WriterTo); ok {
pw := proxyWriterTo{pr, wt}
if b.hasEwma {
rc = ewmaProxyWriterTo{ewmaProxyReader{pr}, pw}
} else {
rc = pw
}
} else if b.hasEwma {
rc = ewmaProxyReader{pr}
} else {
rc = pr
}
return rc
}
func toReadCloser(r io.Reader) io.ReadCloser {
if rc, ok := r.(io.ReadCloser); ok {
return rc
}
return io.NopCloser(r)
}

View file

@ -1,8 +1,8 @@
# Multi Progress Bar
[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
[![GoDoc](https://pkg.go.dev/badge/github.com/vbauerster/mpb)](https://pkg.go.dev/github.com/vbauerster/mpb/v8)
[![Test status](https://github.com/vbauerster/mpb/actions/workflows/test.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
[![Donate with PayPal](https://img.shields.io/badge/Donate-PayPal-green.svg)](https://www.paypal.me/vbauerster)
[![Lint status](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml/badge.svg)](https://github.com/vbauerster/mpb/actions/workflows/golangci-lint.yml)
**mpb** is a Go lib for rendering progress bars in terminal applications.
@ -26,8 +26,8 @@ import (
"math/rand"
"time"
"github.com/vbauerster/mpb/v7"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v8/decor"
)
func main() {
@ -97,9 +97,8 @@ func main() {
// EWMA's unit of measure is an iteration's duration
start := time.Now()
time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
bar.Increment()
// we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract
bar.DecoratorEwmaUpdate(time.Since(start))
// we need to call EwmaIncrement to fulfill ewma decorator's contract
bar.EwmaIncrement(time.Since(start))
}
}()
}

View file

@ -3,43 +3,40 @@ package mpb
import (
"bytes"
"context"
"fmt"
"io"
"runtime/debug"
"strings"
"sync"
"time"
"github.com/acarl005/stripansi"
"github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8/decor"
)
// Bar represents a progress bar.
type Bar struct {
index int // used by heap
priority int // used by heap
hasEwma bool
frameCh chan *renderFrame
operateState chan func(*bState)
done chan struct{}
container *Progress
bs *bState
cancel func()
recoveredPanic interface{}
index int // used by heap
priority int // used by heap
frameCh chan *renderFrame
operateState chan func(*bState)
done chan struct{}
container *Progress
bs *bState
cancel func()
}
type extenderFunc func(rows []io.Reader, width int, stat decor.Statistics) []io.Reader
type syncTable [2][]chan int
type extenderFunc func([]io.Reader, decor.Statistics) ([]io.Reader, error)
// bState is actual bar's state.
type bState struct {
id int
priority int
reqWidth int
shutdown int
total int64
current int64
refill int64
lastIncrement int64
trimSpace bool
completed bool
aborted bool
@ -55,7 +52,7 @@ type bState struct {
filler BarFiller
middleware func(BarFiller) BarFiller
extender extenderFunc
debugOut io.Writer
manualRefresh chan interface{}
wait struct {
bar *Bar // key for (*pState).queueBars
@ -65,7 +62,8 @@ type bState struct {
type renderFrame struct {
rows []io.Reader
shutdown int
shutdown bool
err error
}
func newBar(container *Progress, bs *bState) *Bar {
@ -73,7 +71,6 @@ func newBar(container *Progress, bs *bState) *Bar {
bar := &Bar{
priority: bs.priority,
hasEwma: len(bs.ewmaDecorators) != 0,
frameCh: make(chan *renderFrame, 1),
operateState: make(chan func(*bState)),
done: make(chan struct{}),
@ -85,20 +82,37 @@ func newBar(container *Progress, bs *bState) *Bar {
return bar
}
// ProxyReader wraps r with metrics required for progress tracking.
// If r is 'unknown total/size' reader it's mandatory to call
// (*Bar).SetTotal(-1, true) method after (Reader).Read returns io.EOF.
// Panics if r is nil. If bar is already completed or aborted, returns
// nil.
// ProxyReader wraps io.Reader with metrics required for progress tracking.
// If `r` is 'unknown total/size' reader it's mandatory to call
// (*Bar).SetTotal(-1, true) method after (io.Reader).Read returns io.EOF.
// If bar is already completed or aborted, returns nil.
// Panics if `r` is nil.
func (b *Bar) ProxyReader(r io.Reader) io.ReadCloser {
if r == nil {
panic("expected non nil io.Reader")
}
result := make(chan bool)
select {
case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
return newProxyReader(r, b, <-result)
case <-b.done:
return nil
}
}
// ProxyWriter wraps io.Writer with metrics required for progress tracking.
// If bar is already completed or aborted, returns nil.
// Panics if `w` is nil.
func (b *Bar) ProxyWriter(w io.Writer) io.WriteCloser {
if w == nil {
panic("expected non nil io.Writer")
}
result := make(chan bool)
select {
case b.operateState <- func(s *bState) { result <- len(s.ewmaDecorators) != 0 }:
return newProxyWriter(w, b, <-result)
case <-b.done:
return nil
default:
return b.newProxyReader(r)
}
}
@ -142,7 +156,8 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
sync := make(chan struct{})
select {
case b.operateState <- func(s *bState) {
for _, decorators := range [...][]decor.Decorator{
defer close(sync)
for _, decorators := range [][]decor.Decorator{
s.pDecorators,
s.aDecorators,
} {
@ -150,7 +165,6 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
cb(extractBaseDecorator(d))
}
}
close(sync)
}:
<-sync
case <-b.done:
@ -158,7 +172,7 @@ func (b *Bar) TraverseDecorators(cb func(decor.Decorator)) {
}
// EnableTriggerComplete enables triggering complete event. It's
// effective only for bar which was constructed with `total <= 0` and
// effective only for bars which were constructed with `total <= 0` and
// after total has been set with (*Bar).SetTotal(int64, false). If bar
// has been incremented to the total, complete event is triggered right
// away.
@ -171,7 +185,7 @@ func (b *Bar) EnableTriggerComplete() {
if s.current >= s.total {
s.current = s.total
s.completed = true
go b.forceRefresh()
b.forceRefresh(s.manualRefresh)
} else {
s.triggerComplete = true
}
@ -182,7 +196,7 @@ func (b *Bar) EnableTriggerComplete() {
// SetTotal sets total to an arbitrary value. It's effective only for
// bar which was constructed with `total <= 0`. Setting total to negative
// value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool).
// value is equivalent to (*Bar).SetTotal((*Bar).Current(), bool) but faster.
// If triggerCompleteNow is true, total value is set to current and
// complete event is triggered right away.
func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
@ -199,7 +213,7 @@ func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
if triggerCompleteNow {
s.current = s.total
s.completed = true
go b.forceRefresh()
b.forceRefresh(s.manualRefresh)
}
}:
case <-b.done:
@ -207,16 +221,39 @@ func (b *Bar) SetTotal(total int64, triggerCompleteNow bool) {
}
// SetCurrent sets progress' current to an arbitrary value.
// Setting a negative value will cause a panic.
func (b *Bar) SetCurrent(current int64) {
if current < 0 {
return
}
select {
case b.operateState <- func(s *bState) {
s.lastIncrement = current - s.current
s.current = current
if s.triggerComplete && s.current >= s.total {
s.current = s.total
s.completed = true
go b.forceRefresh()
b.forceRefresh(s.manualRefresh)
}
}:
case <-b.done:
}
}
// EwmaSetCurrent sets progress' current to an arbitrary value and updates
// EWMA based decorators by dur of a single iteration.
func (b *Bar) EwmaSetCurrent(current int64, iterDur time.Duration) {
if current < 0 {
return
}
select {
case b.operateState <- func(s *bState) {
if n := current - s.current; n > 0 {
s.ewmaUpdate(n, iterDur)
}
s.current = current
if s.triggerComplete && s.current >= s.total {
s.current = s.total
s.completed = true
b.forceRefresh(s.manualRefresh)
}
}:
case <-b.done:
@ -240,37 +277,44 @@ func (b *Bar) IncrInt64(n int64) {
}
select {
case b.operateState <- func(s *bState) {
s.lastIncrement = n
s.current += n
if s.triggerComplete && s.current >= s.total {
s.current = s.total
s.completed = true
go b.forceRefresh()
b.forceRefresh(s.manualRefresh)
}
}:
case <-b.done:
}
}
// DecoratorEwmaUpdate updates all EWMA based decorators. Should be
// called on each iteration, because EWMA's unit of measure is an
// iteration's duration. Panics if called before *Bar.Incr... family
// methods.
func (b *Bar) DecoratorEwmaUpdate(dur time.Duration) {
// EwmaIncrement is a shorthand for b.EwmaIncrInt64(1, iterDur).
func (b *Bar) EwmaIncrement(iterDur time.Duration) {
b.EwmaIncrInt64(1, iterDur)
}
// EwmaIncrBy is a shorthand for b.EwmaIncrInt64(int64(n), iterDur).
func (b *Bar) EwmaIncrBy(n int, iterDur time.Duration) {
b.EwmaIncrInt64(int64(n), iterDur)
}
// EwmaIncrInt64 increments progress by amount of n and updates EWMA based
// decorators by dur of a single iteration.
func (b *Bar) EwmaIncrInt64(n int64, iterDur time.Duration) {
if n <= 0 {
return
}
select {
case b.operateState <- func(s *bState) {
if s.lastIncrement > 0 {
s.decoratorEwmaUpdate(dur)
s.lastIncrement = 0
} else {
panic("increment required before ewma iteration update")
s.ewmaUpdate(n, iterDur)
s.current += n
if s.triggerComplete && s.current >= s.total {
s.current = s.total
s.completed = true
b.forceRefresh(s.manualRefresh)
}
}:
case <-b.done:
if b.bs.lastIncrement > 0 {
b.bs.decoratorEwmaUpdate(dur)
b.bs.lastIncrement = 0
}
}
}
@ -305,7 +349,7 @@ func (b *Bar) Abort(drop bool) {
}
s.aborted = true
s.dropOnComplete = drop
go b.forceRefresh()
b.forceRefresh(s.manualRefresh)
}:
case <-b.done:
}
@ -333,6 +377,20 @@ func (b *Bar) Completed() bool {
}
}
// IsRunning reports whether the bar is running, i.e. not yet completed
// and not yet aborted.
func (b *Bar) IsRunning() bool {
result := make(chan bool)
select {
case b.operateState <- func(s *bState) {
result <- !s.completed && !s.aborted
}:
return <-result
case <-b.done:
return false
}
}
// Wait blocks until bar is completed or aborted.
func (b *Bar) Wait() {
<-b.done
@ -358,74 +416,54 @@ func (b *Bar) serve(ctx context.Context, bs *bState) {
}
func (b *Bar) render(tw int) {
select {
case b.operateState <- func(s *bState) {
var done bool
fn := func(s *bState) {
var rows []io.Reader
stat := newStatistics(tw, s)
defer func() {
// recovering if user defined decorator panics for example
if p := recover(); p != nil {
if s.debugOut != nil {
for _, fn := range []func() (int, error){
func() (int, error) {
return fmt.Fprintln(s.debugOut, p)
},
func() (int, error) {
return s.debugOut.Write(debug.Stack())
},
} {
if _, err := fn(); err != nil {
panic(err)
}
}
}
s.aborted = !s.completed
s.extender = makePanicExtender(p)
b.recoveredPanic = p
}
if fn := s.extender; fn != nil {
rows = fn(rows, s.reqWidth, stat)
}
frame := &renderFrame{
rows: rows,
}
if s.completed || s.aborted {
b.cancel()
frame.shutdown++
}
b.frameCh <- frame
}()
if b.recoveredPanic == nil {
rows = append(rows, s.draw(stat))
r, err := s.draw(stat)
if err != nil {
b.frameCh <- &renderFrame{err: err}
return
}
}:
case <-b.done:
var rows []io.Reader
s, stat := b.bs, newStatistics(tw, b.bs)
if b.recoveredPanic == nil {
rows = append(rows, s.draw(stat))
rows = append(rows, r)
if s.extender != nil {
rows, err = s.extender(rows, stat)
if err != nil {
b.frameCh <- &renderFrame{err: err}
return
}
}
if fn := s.extender; fn != nil {
rows = fn(rows, s.reqWidth, stat)
}
frame := &renderFrame{
rows: rows,
frame := &renderFrame{rows: rows}
if s.completed || s.aborted {
frame.shutdown = !done || s.shutdown == 1
b.cancel()
}
b.frameCh <- frame
}
select {
case b.operateState <- fn:
case <-b.done:
done = true
fn(b.bs)
}
}
func (b *Bar) forceRefresh() {
func (b *Bar) forceRefresh(refreshCh chan interface{}) {
b.container.bwg.Add(1)
go b.forceRefreshImpl(refreshCh)
}
func (b *Bar) forceRefreshImpl(refreshCh chan interface{}) {
defer b.container.bwg.Done()
var anyOtherRunning bool
b.container.traverseBars(func(bar *Bar) bool {
anyOtherRunning = b != bar && bar.isRunning()
anyOtherRunning = b != bar && bar.IsRunning()
return !anyOtherRunning
})
if !anyOtherRunning {
for {
select {
case b.container.refreshCh <- time.Now():
time.Sleep(prr)
case refreshCh <- time.Now():
case <-b.done:
return
}
@ -433,20 +471,8 @@ func (b *Bar) forceRefresh() {
}
}
func (b *Bar) isRunning() bool {
result := make(chan bool)
select {
case b.operateState <- func(s *bState) {
result <- !s.completed && !s.aborted
}:
return <-result
case <-b.done:
return false
}
}
func (b *Bar) wSyncTable() [][]chan int {
result := make(chan [][]chan int)
func (b *Bar) wSyncTable() syncTable {
result := make(chan syncTable)
select {
case b.operateState <- func(s *bState) { result <- s.wSyncTable() }:
return <-result
@ -455,68 +481,111 @@ func (b *Bar) wSyncTable() [][]chan int {
}
}
func (s *bState) draw(stat decor.Statistics) io.Reader {
bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2]
nlr := bytes.NewReader([]byte("\n"))
tw := stat.AvailableWidth
for _, d := range s.pDecorators {
str := d.Decor(stat)
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
bufP.WriteString(str)
func (s *bState) draw(stat decor.Statistics) (io.Reader, error) {
r, err := s.drawImpl(stat)
if err != nil {
for _, b := range s.buffers {
b.Reset()
}
return nil, err
}
if stat.AvailableWidth < 1 {
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), tw, "…"))
bufP.Reset()
return io.MultiReader(trunc, nlr)
}
if !s.trimSpace && stat.AvailableWidth > 1 {
stat.AvailableWidth -= 2
bufB.WriteByte(' ')
defer bufB.WriteByte(' ')
}
tw = stat.AvailableWidth
for _, d := range s.aDecorators {
str := d.Decor(stat)
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
bufA.WriteString(str)
}
if stat.AvailableWidth < 1 {
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), tw, "…"))
bufA.Reset()
return io.MultiReader(bufP, bufB, trunc, nlr)
}
s.filler.Fill(bufB, s.reqWidth, stat)
return io.MultiReader(bufP, bufB, bufA, nlr)
return io.MultiReader(r, strings.NewReader("\n")), nil
}
func (s *bState) wSyncTable() [][]chan int {
columns := make([]chan int, 0, len(s.pDecorators)+len(s.aDecorators))
var pCount int
for _, d := range s.pDecorators {
if ch, ok := d.Sync(); ok {
columns = append(columns, ch)
pCount++
func (s *bState) drawImpl(stat decor.Statistics) (io.Reader, error) {
decorFiller := func(buf *bytes.Buffer, decorators []decor.Decorator) (res struct {
width int
truncate bool
err error
}) {
res.width = stat.AvailableWidth
for _, d := range decorators {
str := d.Decor(stat)
if stat.AvailableWidth > 0 {
stat.AvailableWidth -= runewidth.StringWidth(stripansi.Strip(str))
if res.err == nil {
_, res.err = buf.WriteString(str)
}
}
}
res.truncate = stat.AvailableWidth < 0
return res
}
bufP, bufB, bufA := s.buffers[0], s.buffers[1], s.buffers[2]
resP := decorFiller(bufP, s.pDecorators)
resA := decorFiller(bufA, s.aDecorators)
for _, err := range []error{resP.err, resA.err} {
if err != nil {
return nil, err
}
}
var aCount int
for _, d := range s.aDecorators {
if ch, ok := d.Sync(); ok {
columns = append(columns, ch)
aCount++
if resP.truncate {
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufP.String()), resP.width, "…"))
bufP.Reset()
bufA.Reset()
return trunc, nil
}
if resA.truncate {
trunc := strings.NewReader(runewidth.Truncate(stripansi.Strip(bufA.String()), resA.width, "…"))
bufA.Reset()
return io.MultiReader(bufP, trunc), nil
}
if !s.trimSpace && stat.AvailableWidth >= 2 {
stat.AvailableWidth -= 2
writeFiller := func(buf *bytes.Buffer) error {
return s.filler.Fill(buf, stat)
}
for _, fn := range []func(*bytes.Buffer) error{
writeSpace,
writeFiller,
writeSpace,
} {
if err := fn(bufB); err != nil {
return nil, err
}
}
} else {
err := s.filler.Fill(bufB, stat)
if err != nil {
return nil, err
}
}
return io.MultiReader(bufP, bufB, bufA), nil
}
func (s *bState) wSyncTable() (table syncTable) {
var count int
var row []chan int
for i, decorators := range [][]decor.Decorator{
s.pDecorators,
s.aDecorators,
} {
for _, d := range decorators {
if ch, ok := d.Sync(); ok {
row = append(row, ch)
count++
}
}
switch i {
case 0:
table[i] = row[0:count]
default:
table[i] = row[len(table[i-1]):count]
}
}
table := make([][]chan int, 2)
table[0] = columns[0:pCount]
table[1] = columns[pCount : pCount+aCount : pCount+aCount]
return table
}
func (s *bState) subscribeDecorators() {
for _, decorators := range [...][]decor.Decorator{
for _, decorators := range [][]decor.Decorator{
s.pDecorators,
s.aDecorators,
} {
@ -535,16 +604,16 @@ func (s *bState) subscribeDecorators() {
}
}
func (s bState) decoratorEwmaUpdate(dur time.Duration) {
wg := new(sync.WaitGroup)
func (s bState) ewmaUpdate(n int64, dur time.Duration) {
var wg sync.WaitGroup
for i := 0; i < len(s.ewmaDecorators); i++ {
switch d := s.ewmaDecorators[i]; i {
case len(s.ewmaDecorators) - 1:
d.EwmaUpdate(s.lastIncrement, dur)
d.EwmaUpdate(n, dur)
default:
wg.Add(1)
go func() {
d.EwmaUpdate(s.lastIncrement, dur)
d.EwmaUpdate(n, dur)
wg.Done()
}()
}
@ -553,7 +622,7 @@ func (s bState) decoratorEwmaUpdate(dur time.Duration) {
}
func (s bState) decoratorAverageAdjust(start time.Time) {
wg := new(sync.WaitGroup)
var wg sync.WaitGroup
for i := 0; i < len(s.averageDecorators); i++ {
switch d := s.averageDecorators[i]; i {
case len(s.averageDecorators) - 1:
@ -570,7 +639,7 @@ func (s bState) decoratorAverageAdjust(start time.Time) {
}
func (s bState) decoratorShutdownNotify() {
wg := new(sync.WaitGroup)
var wg sync.WaitGroup
for i := 0; i < len(s.shutdownListeners); i++ {
switch d := s.shutdownListeners[i]; i {
case len(s.shutdownListeners) - 1:
@ -589,6 +658,7 @@ func (s bState) decoratorShutdownNotify() {
func newStatistics(tw int, s *bState) decor.Statistics {
return decor.Statistics{
AvailableWidth: tw,
RequestedWidth: s.reqWidth,
ID: s.id,
Total: s.total,
Current: s.current,
@ -605,13 +675,6 @@ func extractBaseDecorator(d decor.Decorator) decor.Decorator {
return d
}
func makePanicExtender(p interface{}) extenderFunc {
pstr := fmt.Sprint(p)
return func(rows []io.Reader, _ int, stat decor.Statistics) []io.Reader {
r := io.MultiReader(
strings.NewReader(runewidth.Truncate(pstr, stat.AvailableWidth, "…")),
bytes.NewReader([]byte("\n")),
)
return append(rows, r)
}
func writeSpace(buf *bytes.Buffer) error {
return buf.WriteByte(' ')
}

View file

@ -3,17 +3,13 @@ package mpb
import (
"io"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8/decor"
)
// BarFiller interface.
// Bar (without decorators) renders itself by calling BarFiller's Fill method.
//
// reqWidth is requested width set by `func WithWidth(int) ContainerOption`.
// If not set, it defaults to terminal width.
//
type BarFiller interface {
Fill(w io.Writer, reqWidth int, stat decor.Statistics)
Fill(io.Writer, decor.Statistics) error
}
// BarFillerBuilder interface.
@ -22,17 +18,16 @@ type BarFiller interface {
// BarStyle()
// SpinnerStyle()
// NopStyle()
//
type BarFillerBuilder interface {
Build() BarFiller
}
// BarFillerFunc is function type adapter to convert compatible function
// into BarFiller interface.
type BarFillerFunc func(w io.Writer, reqWidth int, stat decor.Statistics)
type BarFillerFunc func(io.Writer, decor.Statistics) error
func (f BarFillerFunc) Fill(w io.Writer, reqWidth int, stat decor.Statistics) {
f(w, reqWidth, stat)
func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error {
return f(w, stat)
}
// BarFillerBuilderFunc is function type adapter to convert compatible
@ -42,9 +37,3 @@ type BarFillerBuilderFunc func() BarFiller
func (f BarFillerBuilderFunc) Build() BarFiller {
return f()
}
// NewBarFiller constructs a BarFiller from provided BarFillerBuilder.
// Deprecated. Prefer using `*Progress.New(...)` directly.
func NewBarFiller(b BarFillerBuilder) BarFiller {
return b.Build()
}

View file

@ -5,8 +5,8 @@ import (
"github.com/acarl005/stripansi"
"github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v7/internal"
"github.com/vbauerster/mpb/v8/decor"
"github.com/vbauerster/mpb/v8/internal"
)
const (
@ -36,8 +36,8 @@ type bFiller struct {
components [components]*component
tip struct {
count uint
onComplete *component
frames []*component
onComplete *component
}
}
@ -148,20 +148,22 @@ func (s *barStyle) Build() BarFiller {
return bf
}
func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
brackets := s.components[iLbound].width + s.components[iRbound].width
func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
// don't count brackets as progress
width -= brackets
width -= (s.components[iLbound].width + s.components[iRbound].width)
if width < 0 {
return
return nil
}
mustWrite(w, s.components[iLbound].bytes)
defer mustWrite(w, s.components[iRbound].bytes)
_, err = w.Write(s.components[iLbound].bytes)
if err != nil {
return err
}
if width == 0 {
return
_, err = w.Write(s.components[iRbound].bytes)
return err
}
var filling [][]byte
@ -171,7 +173,7 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
var refWidth int
curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))
if stat.Current >= stat.Total {
if stat.Completed {
tip = s.tip.onComplete
} else {
tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))]
@ -230,24 +232,28 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
}
if s.rev {
flush(w, padding, filling)
} else {
flush(w, filling, padding)
filling, padding = padding, filling
}
err = flush(w, filling, padding)
if err != nil {
return err
}
_, err = w.Write(s.components[iRbound].bytes)
return err
}
func flush(w io.Writer, filling, padding [][]byte) {
func flush(w io.Writer, filling, padding [][]byte) error {
for i := len(filling) - 1; i >= 0; i-- {
mustWrite(w, filling[i])
_, err := w.Write(filling[i])
if err != nil {
return err
}
}
for i := 0; i < len(padding); i++ {
mustWrite(w, padding[i])
}
}
func mustWrite(w io.Writer, p []byte) {
_, err := w.Write(p)
if err != nil {
panic(err)
_, err := w.Write(padding[i])
if err != nil {
return err
}
}
return nil
}

View file

@ -3,12 +3,14 @@ package mpb
import (
"io"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8/decor"
)
// NopStyle provides BarFillerBuilder which builds NOP BarFiller.
func NopStyle() BarFillerBuilder {
return BarFillerBuilderFunc(func() BarFiller {
return BarFillerFunc(func(io.Writer, int, decor.Statistics) {})
return BarFillerFunc(func(io.Writer, decor.Statistics) error {
return nil
})
})
}

View file

@ -6,8 +6,8 @@ import (
"github.com/acarl005/stripansi"
"github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v7/internal"
"github.com/vbauerster/mpb/v8/decor"
"github.com/vbauerster/mpb/v8/internal"
)
const (
@ -63,17 +63,16 @@ func (s *spinnerStyle) Build() BarFiller {
return sf
}
func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
width = internal.CheckRequestedWidth(width, stat.AvailableWidth)
func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
frame := s.frames[s.count%uint(len(s.frames))]
frameWidth := runewidth.StringWidth(stripansi.Strip(frame))
if width < frameWidth {
return
return nil
}
var err error
rest := width - frameWidth
switch s.position {
case positionLeft:
@ -84,8 +83,6 @@ func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
_, err = io.WriteString(w, str)
}
if err != nil {
panic(err)
}
s.count++
return err
}

View file

@ -4,44 +4,41 @@ import (
"bytes"
"io"
"github.com/vbauerster/mpb/v7/decor"
"github.com/vbauerster/mpb/v8/decor"
)
// BarOption is a func option to alter default behavior of a bar.
type BarOption func(*bState)
func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) {
for _, d := range decorators {
if d != nil {
filtered = append(filtered, d)
}
}
return
}
func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
func inspect(decorators []decor.Decorator) (dest []decor.Decorator) {
type mergeWrapper interface {
MergeUnwrap() []decor.Decorator
}
for _, decorator := range decorators {
if mw, ok := decorator.(mergeWrapper); ok {
*dest = append(*dest, mw.MergeUnwrap()...)
if decorator == nil {
continue
}
*dest = append(*dest, decorator)
if mw, ok := decorator.(mergeWrapper); ok {
dest = append(dest, mw.MergeUnwrap()...)
}
dest = append(dest, decorator)
}
return
}
// AppendDecorators let you inject decorators to the bar's right side.
func AppendDecorators(decorators ...decor.Decorator) BarOption {
decorators = inspect(decorators)
return func(s *bState) {
s.addDecorators(&s.aDecorators, skipNil(decorators)...)
s.aDecorators = decorators
}
}
// PrependDecorators let you inject decorators to the bar's left side.
func PrependDecorators(decorators ...decor.Decorator) BarOption {
decorators = inspect(decorators)
return func(s *bState) {
s.addDecorators(&s.pDecorators, skipNil(decorators)...)
s.pDecorators = decorators
}
}
@ -91,15 +88,12 @@ func BarFillerClearOnComplete() BarOption {
// BarFillerOnComplete replaces bar's filler with message, on complete event.
func BarFillerOnComplete(message string) BarOption {
return BarFillerMiddleware(func(base BarFiller) BarFiller {
return BarFillerFunc(func(w io.Writer, reqWidth int, st decor.Statistics) {
return BarFillerFunc(func(w io.Writer, st decor.Statistics) error {
if st.Completed {
_, err := io.WriteString(w, message)
if err != nil {
panic(err)
}
} else {
base.Fill(w, reqWidth, st)
return err
}
return base.Fill(w, st)
})
})
}
@ -121,32 +115,26 @@ func BarPriority(priority int) BarOption {
// BarExtender extends bar with arbitrary lines. Provided BarFiller will be
// called at each render/flush cycle. Any lines written to the underlying
// io.Writer will be printed after the bar itself.
func BarExtender(filler BarFiller) BarOption {
return barExtender(filler, false)
}
// BarExtenderRev extends bar with arbitrary lines in reverse order. Provided
// BarFiller will be called at each render/flush cycle. Any lines written
// to the underlying io.Writer will be printed before the bar itself.
func BarExtenderRev(filler BarFiller) BarOption {
return barExtender(filler, true)
}
func barExtender(filler BarFiller, rev bool) BarOption {
// io.Writer will extend the bar either in above (rev = true) or below
// (rev = false) direction.
func BarExtender(filler BarFiller, rev bool) BarOption {
if filler == nil {
return nil
}
fn := makeExtenderFunc(filler, rev)
return func(s *bState) {
s.extender = makeExtenderFunc(filler, rev)
s.extender = fn
}
}
func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
buf := new(bytes.Buffer)
base := func(rows []io.Reader, width int, stat decor.Statistics) []io.Reader {
buf.Reset()
filler.Fill(buf, width, stat)
base := func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
err := filler.Fill(buf, stat)
if err != nil {
buf.Reset()
return rows, err
}
for {
b, err := buf.ReadBytes('\n')
if err != nil {
@ -154,19 +142,22 @@ func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
}
rows = append(rows, bytes.NewReader(b))
}
return rows
buf.Reset()
return rows, err
}
if !rev {
return base
} else {
return func(rows []io.Reader, width int, stat decor.Statistics) []io.Reader {
rows = base(rows, width, stat)
for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 {
rows[left], rows[right] = rows[right], rows[left]
}
return rows
}
return func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
rows, err := base(rows, stat)
if err != nil {
return rows, err
}
for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 {
rows[left], rows[right] = rows[right], rows[left]
}
return rows, err
}
}
@ -185,7 +176,7 @@ func BarNoPop() BarOption {
}
}
// BarOptional will invoke provided option only when cond is true.
// BarOptional will return provided option only when cond is true.
func BarOptional(option BarOption, cond bool) BarOption {
if cond {
return option
@ -193,11 +184,26 @@ func BarOptional(option BarOption, cond bool) BarOption {
return nil
}
// BarOptOn will invoke provided option only when higher order predicate
// evaluates to true.
// BarOptOn will return provided option only when predicate evaluates to true.
func BarOptOn(option BarOption, predicate func() bool) BarOption {
if predicate() {
return option
}
return nil
}
// BarFuncOptional will call option and return its value only when cond is true.
func BarFuncOptional(option func() BarOption, cond bool) BarOption {
if cond {
return option()
}
return nil
}
// BarFuncOptOn will call option and return its value only when predicate evaluates to true.
func BarFuncOptOn(option func() BarOption, predicate func() bool) BarOption {
if predicate() {
return option()
}
return nil
}

View file

@ -33,15 +33,16 @@ func WithWidth(width int) ContainerOption {
// WithRefreshRate overrides default 150ms refresh rate.
func WithRefreshRate(d time.Duration) ContainerOption {
return func(s *pState) {
s.rr = d
s.refreshRate = d
}
}
// WithManualRefresh disables internal auto refresh time.Ticker.
// Refresh will occur upon receive value from provided ch.
func WithManualRefresh(ch <-chan interface{}) ContainerOption {
func WithManualRefresh(ch chan interface{}) ContainerOption {
return func(s *pState) {
s.externalRefresh = ch
s.manualRefresh = ch
s.disableAutoRefresh = true
}
}
@ -71,34 +72,37 @@ func WithShutdownNotifier(ch chan struct{}) ContainerOption {
// will effectively disable auto refresh rate and discard any output,
// useful if you want to disable progress bars with little overhead.
func WithOutput(w io.Writer) ContainerOption {
var discarded bool
if w == nil {
w = io.Discard
discarded = true
}
return func(s *pState) {
if w == nil {
s.output = io.Discard
s.outputDiscarded = true
return
}
s.output = w
s.outputDiscarded = discarded
}
}
// WithDebugOutput sets debug output.
func WithDebugOutput(w io.Writer) ContainerOption {
if w == nil {
return nil
w = io.Discard
}
return func(s *pState) {
s.debugOut = w
}
}
// PopCompletedMode will pop and stop rendering completed bars.
// PopCompletedMode will pop completed bars to the top.
// To stop rendering bar after it has been popped, use
// mpb.BarRemoveOnComplete() option on that bar.
func PopCompletedMode() ContainerOption {
return func(s *pState) {
s.popCompleted = true
}
}
// ContainerOptional will invoke provided option only when cond is true.
// ContainerOptional will return provided option only when cond is true.
func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
if cond {
return option
@ -106,11 +110,26 @@ func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
return nil
}
// ContainerOptOn will invoke provided option only when higher order
// predicate evaluates to true.
// ContainerOptOn will return provided option only when predicate evaluates to true.
func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption {
if predicate() {
return option
}
return nil
}
// ContainerFuncOptional will call option and return its value only when cond is true.
func ContainerFuncOptional(option func() ContainerOption, cond bool) ContainerOption {
if cond {
return option()
}
return nil
}
// ContainerFuncOptOn will call option and return its value only when predicate evaluates to true.
func ContainerFuncOptOn(option func() ContainerOption, predicate func() bool) ContainerOption {
if predicate() {
return option()
}
return nil
}

View file

@ -1,4 +1,4 @@
// +build darwin dragonfly freebsd netbsd openbsd
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
package cwriter

View file

@ -1,4 +1,4 @@
// +build aix linux
//go:build aix || linux
package cwriter

View file

@ -1,4 +1,4 @@
// +build solaris
//go:build solaris
package cwriter

View file

@ -1,4 +1,4 @@
// +build zos
//go:build zos
package cwriter

54
vendor/github.com/vbauerster/mpb/v8/cwriter/writer.go generated vendored Normal file
View file

@ -0,0 +1,54 @@
package cwriter
import (
"bytes"
"errors"
"io"
"os"
"strconv"
)
// https://github.com/dylanaraps/pure-sh-bible#cursor-movement
const (
escOpen = "\x1b["
cuuAndEd = "A\x1b[J"
)
// ErrNotTTY not a TeleTYpewriter error.
var ErrNotTTY = errors.New("not a terminal")
// New returns a new Writer with defaults.
func New(out io.Writer) *Writer {
w := &Writer{
Buffer: new(bytes.Buffer),
out: out,
termSize: func(_ int) (int, int, error) {
return -1, -1, ErrNotTTY
},
}
if f, ok := out.(*os.File); ok {
w.fd = int(f.Fd())
if IsTerminal(w.fd) {
w.terminal = true
w.termSize = func(fd int) (int, int, error) {
return GetSize(fd)
}
}
}
bb := make([]byte, 16)
w.ew = escWriter(bb[:copy(bb, []byte(escOpen))])
return w
}
// GetTermSize returns WxH of underlying terminal.
func (w *Writer) GetTermSize() (width, height int, err error) {
return w.termSize(w.fd)
}
type escWriter []byte
func (b escWriter) ansiCuuAndEd(out io.Writer, n int) error {
b = strconv.AppendInt(b, int64(n), 10)
_, err := out.Write(append(b, []byte(cuuAndEd)...))
return err
}

View file

@ -0,0 +1,48 @@
//go:build !windows
package cwriter
import (
"bytes"
"io"
"golang.org/x/sys/unix"
)
// Writer is a buffered terminal writer, which moves cursor N lines up
// on each flush except the first one, where N is a number of lines of
// a previous flush.
type Writer struct {
*bytes.Buffer
out io.Writer
ew escWriter
fd int
terminal bool
termSize func(int) (int, int, error)
}
// Flush flushes the underlying buffer.
// It's caller's responsibility to pass correct number of lines.
func (w *Writer) Flush(lines int) error {
_, err := w.WriteTo(w.out)
// some terminals interpret 'cursor up 0' as 'cursor up 1'
if err == nil && lines > 0 {
err = w.ew.ansiCuuAndEd(w, lines)
}
return err
}
// GetSize returns the dimensions of the given terminal.
func GetSize(fd int) (width, height int, err error) {
ws, err := unix.IoctlGetWinsize(fd, unix.TIOCGWINSZ)
if err != nil {
return -1, -1, err
}
return int(ws.Col), int(ws.Row), nil
}
// IsTerminal returns whether the given file descriptor is a terminal.
func IsTerminal(fd int) bool {
_, err := unix.IoctlGetTermios(fd, ioctlReadTermios)
return err == nil
}

View file

@ -1,8 +1,10 @@
// +build windows
//go:build windows
package cwriter
import (
"bytes"
"io"
"unsafe"
"golang.org/x/sys/windows"
@ -15,10 +17,37 @@ var (
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
)
func (w *Writer) clearLines() error {
// Writer is a buffered terminal writer, which moves cursor N lines up
// on each flush except the first one, where N is a number of lines of
// a previous flush.
type Writer struct {
*bytes.Buffer
out io.Writer
ew escWriter
lines int
fd int
terminal bool
termSize func(int) (int, int, error)
}
// Flush flushes the underlying buffer.
// It's caller's responsibility to pass correct number of lines.
func (w *Writer) Flush(lines int) error {
if w.lines > 0 {
err := w.clearLines(w.lines)
if err != nil {
return err
}
}
w.lines = lines
_, err := w.WriteTo(w.out)
return err
}
func (w *Writer) clearLines(n int) error {
if !w.terminal {
// hope it's cygwin or similar
return w.ansiCuuAndEd()
return w.ew.ansiCuuAndEd(w.out, n)
}
var info windows.ConsoleScreenBufferInfo
@ -26,7 +55,7 @@ func (w *Writer) clearLines() error {
return err
}
info.CursorPosition.Y -= int16(w.lines)
info.CursorPosition.Y -= int16(n)
if info.CursorPosition.Y < 0 {
info.CursorPosition.Y = 0
}
@ -40,7 +69,7 @@ func (w *Writer) clearLines() error {
X: info.Window.Left,
Y: info.CursorPosition.Y,
}
count := uint32(info.Size.X) * uint32(w.lines)
count := uint32(info.Size.X) * uint32(n)
_, _, _ = procFillConsoleOutputCharacter.Call(
uintptr(w.fd),
uintptr(' '),
@ -52,7 +81,6 @@ func (w *Writer) clearLines() error {
}
// GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height.
func GetSize(fd int) (width, height int, err error) {
var info windows.ConsoleScreenBufferInfo

View file

@ -6,7 +6,6 @@ package decor
// `fn` DecorFunc callback
//
// `wcc` optional WC config
//
func Any(fn DecorFunc, wcc ...WC) Decorator {
return &any{initWC(wcc...), fn}
}

View file

@ -42,7 +42,6 @@ func CountersKiloByte(pairFmt string, wcc ...WC) Decorator {
// pairFmt="% .1f / % .1f" output: "1.0 MB / 12.0 MB"
// pairFmt="%d / %d" output: "1MB / 12MB"
// pairFmt="% d / % d" output: "1 MB / 12 MB"
//
func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
producer := func(unit int, pairFmt string) DecorFunc {
if pairFmt == "" {
@ -99,7 +98,6 @@ func TotalKiloByte(format string, wcc ...WC) Decorator {
// format="% .1f" output: "12.0 MiB"
// format="%d" output: "12MiB"
// format="% d" output: "12 MiB"
//
func Total(unit int, format string, wcc ...WC) Decorator {
producer := func(unit int, format string) DecorFunc {
if format == "" {
@ -157,7 +155,6 @@ func CurrentKiloByte(format string, wcc ...WC) Decorator {
// format="% .1f" output: "12.0 MiB"
// format="%d" output: "12MiB"
// format="% d" output: "12 MiB"
//
func Current(unit int, format string, wcc ...WC) Decorator {
producer := func(unit int, format string) DecorFunc {
if format == "" {
@ -215,7 +212,6 @@ func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator {
// format="% .1f" output: "12.0 MiB"
// format="%d" output: "12MiB"
// format="% d" output: "12 MiB"
//
func InvertedCurrent(unit int, format string, wcc ...WC) Decorator {
producer := func(unit int, format string) DecorFunc {
if format == "" {

View file

@ -44,10 +44,11 @@ const (
ET_STYLE_MMSS
)
// Statistics consists of progress related statistics, that Decorator
// may need.
// Statistics contains fields which are necessary for implementing
// `decor.Decorator` and `mpb.BarFiller` interfaces.
type Statistics struct {
AvailableWidth int
AvailableWidth int // calculated width initially equal to terminal width
RequestedWidth int // width set by `mpb.WithWidth`
ID int
Total int64
Current int64
@ -138,17 +139,17 @@ type WC struct {
// Should be called by any Decorator implementation.
func (wc *WC) FormatMsg(msg string) string {
pureWidth := runewidth.StringWidth(msg)
stripWidth := runewidth.StringWidth(stripansi.Strip(msg))
maxCell := wc.W
viewWidth := runewidth.StringWidth(stripansi.Strip(msg))
max := wc.W
if (wc.C & DSyncWidth) != 0 {
cellCount := stripWidth
viewWidth := viewWidth
if (wc.C & DextraSpace) != 0 {
cellCount++
viewWidth++
}
wc.wsync <- cellCount
maxCell = <-wc.wsync
wc.wsync <- viewWidth
max = <-wc.wsync
}
return wc.fill(msg, maxCell+(pureWidth-stripWidth))
return wc.fill(msg, max-viewWidth+pureWidth)
}
// Init initializes width related config.

View file

@ -1,4 +1,4 @@
// Package decor provides common decorators for "github.com/vbauerster/mpb/v7" module.
// Package decor provides common decorators for "github.com/vbauerster/mpb/v8" module.
//
// Some decorators returned by this package might have a closure state. It is ok to use
// decorators concurrently, unless you share the same decorator among multiple
@ -6,10 +6,10 @@
//
// Don't:
//
// p := mpb.New()
// name := decor.Name("bar")
// p.AddBar(100, mpb.AppendDecorators(name))
// p.AddBar(100, mpb.AppendDecorators(name))
// p := mpb.New()
// name := decor.Name("bar")
// p.AddBar(100, mpb.AppendDecorators(name))
// p.AddBar(100, mpb.AppendDecorators(name))
//
// Do:
//

View file

@ -9,7 +9,6 @@ import (
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
//
func Elapsed(style TimeStyle, wcc ...WC) Decorator {
return NewElapsed(style, time.Now(), wcc...)
}
@ -21,7 +20,6 @@ func Elapsed(style TimeStyle, wcc ...WC) Decorator {
// `startTime` start time
//
// `wcc` optional WC config
//
func NewElapsed(style TimeStyle, startTime time.Time, wcc ...WC) Decorator {
var msg string
producer := chooseTimeProducer(style)

View file

@ -22,10 +22,9 @@ func (f TimeNormalizerFunc) Normalize(src time.Duration) time.Duration {
return f(src)
}
// EwmaETA exponential-weighted-moving-average based ETA decorator.
// For this decorator to work correctly you have to measure each
// iteration's duration and pass it to the
// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
// EwmaETA exponential-weighted-moving-average based ETA decorator. For this
// decorator to work correctly you have to measure each iteration's duration
// and pass it to one of the (*Bar).EwmaIncr... family methods.
func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
var average ewma.MovingAverage
if age == 0 {
@ -45,7 +44,6 @@ func EwmaETA(style TimeStyle, age float64, wcc ...WC) Decorator {
// `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
//
// `wcc` optional WC config
//
func MovingAverageETA(style TimeStyle, average ewma.MovingAverage, normalizer TimeNormalizer, wcc ...WC) Decorator {
d := &movingAverageETA{
WC: initWC(wcc...),
@ -85,7 +83,6 @@ func (d *movingAverageETA) EwmaUpdate(n int64, dur time.Duration) {
// `style` one of [ET_STYLE_GO|ET_STYLE_HHMMSS|ET_STYLE_HHMM|ET_STYLE_MMSS]
//
// `wcc` optional WC config
//
func AverageETA(style TimeStyle, wcc ...WC) Decorator {
return NewAverageETA(style, time.Now(), nil, wcc...)
}
@ -99,7 +96,6 @@ func AverageETA(style TimeStyle, wcc ...WC) Decorator {
// `normalizer` available implementations are [FixedIntervalTimeNormalizer|MaxTolerateTimeNormalizer]
//
// `wcc` optional WC config
//
func NewAverageETA(style TimeStyle, startTime time.Time, normalizer TimeNormalizer, wcc ...WC) Decorator {
d := &averageETA{
WC: initWC(wcc...),

View file

@ -10,12 +10,11 @@ import (
// Merge wraps its decorator argument with intention to sync width
// with several decorators of another bar. Visual example:
//
// +----+--------+---------+--------+
// | B1 | MERGE(D, P1, Pn) |
// +----+--------+---------+--------+
// | B2 | D0 | D1 | Dn |
// +----+--------+---------+--------+
//
// +----+--------+---------+--------+
// | B1 | MERGE(D, P1, Pn) |
// +----+--------+---------+--------+
// | B2 | D0 | D1 | Dn |
// +----+--------+---------+--------+
func Merge(decorator Decorator, placeholders ...WC) Decorator {
if decorator == nil {
return nil

View file

@ -6,7 +6,6 @@ package decor
// `str` string to display
//
// `wcc` optional WC config
//
func Name(str string, wcc ...WC) Decorator {
return Any(func(Statistics) string { return str }, wcc...)
}

View file

@ -7,7 +7,6 @@ package decor
// `decorator` Decorator to wrap
//
// `message` message to display on abort event
//
func OnAbort(decorator Decorator, message string) Decorator {
if decorator == nil {
return nil

View file

@ -6,7 +6,6 @@ package decor
// `decorator` Decorator to wrap
//
// `message` message to display on complete event
//
func OnComplete(decorator Decorator, message string) Decorator {
if decorator == nil {
return nil

View file

@ -5,7 +5,6 @@ package decor
// `decorator` Decorator
//
// `cond` bool
//
func OnCondition(decorator Decorator, cond bool) Decorator {
return Conditional(cond, decorator, nil)
}
@ -15,7 +14,6 @@ func OnCondition(decorator Decorator, cond bool) Decorator {
// `decorator` Decorator
//
// `predicate` func() bool
//
func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
return Predicative(predicate, decorator, nil)
}
@ -28,7 +26,6 @@ func OnPredicate(decorator Decorator, predicate func() bool) Decorator {
// `a` Decorator
//
// `b` Decorator
//
func Conditional(cond bool, a, b Decorator) Decorator {
if cond {
return a
@ -45,7 +42,6 @@ func Conditional(cond bool, a, b Decorator) Decorator {
// `a` Decorator
//
// `b` Decorator
//
func Predicative(predicate func() bool, a, b Decorator) Decorator {
if predicate() {
return a

View file

@ -4,7 +4,7 @@ import (
"fmt"
"strconv"
"github.com/vbauerster/mpb/v7/internal"
"github.com/vbauerster/mpb/v8/internal"
)
type percentageType float64
@ -23,11 +23,18 @@ func (s percentageType) Format(st fmt.State, verb rune) {
}
}
mustWriteString(st, strconv.FormatFloat(float64(s), 'f', prec, 64))
p := bytePool.Get().(*[]byte)
b := strconv.AppendFloat(*p, float64(s), 'f', prec, 64)
if st.Flag(' ') {
mustWriteString(st, " ")
b = append(b, ' ', '%')
} else {
b = append(b, '%')
}
mustWriteString(st, "%")
_, err := st.Write(b)
if err != nil {
panic(err)
}
bytePool.Put(p)
}
// Percentage returns percentage decorator. It's a wrapper of NewPercentage.
@ -43,7 +50,6 @@ func Percentage(wcc ...WC) Decorator {
// format="% .1f" output: "1.0 %"
// format="%d" output: "1%"
// format="% d" output: "1 %"
//
func NewPercentage(format string, wcc ...WC) Decorator {
if format == "" {
format = "% d"

10
vendor/github.com/vbauerster/mpb/v8/decor/pool.go generated vendored Normal file
View file

@ -0,0 +1,10 @@
package decor
import "sync"
var bytePool = sync.Pool{
New: func() interface{} {
b := make([]byte, 0, 16)
return &b
},
}

View file

@ -49,11 +49,17 @@ func (self SizeB1024) Format(st fmt.State, verb rune) {
unit = _iTiB
}
mustWriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
p := bytePool.Get().(*[]byte)
b := strconv.AppendFloat(*p, float64(self)/float64(unit), 'f', prec, 64)
if st.Flag(' ') {
mustWriteString(st, " ")
b = append(b, ' ')
}
mustWriteString(st, unit.String())
b = append(b, []byte(unit.String())...)
_, err := st.Write(b)
if err != nil {
panic(err)
}
bytePool.Put(p)
}
const (
@ -97,9 +103,15 @@ func (self SizeB1000) Format(st fmt.State, verb rune) {
unit = _TB
}
mustWriteString(st, strconv.FormatFloat(float64(self)/float64(unit), 'f', prec, 64))
p := bytePool.Get().(*[]byte)
b := strconv.AppendFloat(*p, float64(self)/float64(unit), 'f', prec, 64)
if st.Flag(' ') {
mustWriteString(st, " ")
b = append(b, ' ')
}
mustWriteString(st, unit.String())
b = append(b, []byte(unit.String())...)
_, err := st.Write(b)
if err != nil {
panic(err)
}
bytePool.Put(p)
}

View file

@ -12,7 +12,6 @@ import (
// used with SizeB1000 or SizeB1024 types, for example:
//
// fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048)))
//
func FmtAsSpeed(input fmt.Formatter) fmt.Formatter {
return &speedFormatter{input}
}
@ -23,13 +22,15 @@ type speedFormatter struct {
func (self *speedFormatter) Format(st fmt.State, verb rune) {
self.Formatter.Format(st, verb)
mustWriteString(st, "/s")
_, err := st.Write([]byte("/s"))
if err != nil {
panic(err)
}
}
// EwmaSpeed exponential-weighted-moving-average based speed decorator.
// For this decorator to work correctly you have to measure each
// iteration's duration and pass it to the
// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
// For this decorator to work correctly you have to measure each iteration's
// duration and pass it to one of the (*Bar).EwmaIncr... family methods.
func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
var average ewma.MovingAverage
if age == 0 {
@ -57,7 +58,6 @@ func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
// unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
// unit=UnitKB, format="%.1f" output: "1.0MB/s"
// unit=UnitKB, format="% .1f" output: "1.0 MB/s"
//
func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
if format == "" {
format = "%.0f"
@ -119,7 +119,6 @@ func AverageSpeed(unit int, format string, wcc ...WC) Decorator {
// unit=UnitKiB, format="% .1f" output: "1.0 MiB/s"
// unit=UnitKB, format="%.1f" output: "1.0MB/s"
// unit=UnitKB, format="% .1f" output: "1.0 MB/s"
//
func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
if format == "" {
format = "%.0f"

View file

@ -11,23 +11,26 @@ import (
"sync"
"time"
"github.com/vbauerster/mpb/v7/cwriter"
"github.com/vbauerster/mpb/v8/cwriter"
)
const (
prr = 150 * time.Millisecond // default RefreshRate
defaultRefreshRate = 150 * time.Millisecond
)
// DoneError represents an error when `*mpb.Progress` is done but its functionality is requested.
var DoneError = fmt.Errorf("%T instance can't be reused after it's done!", (*Progress)(nil))
// Progress represents a container that renders one or more progress bars.
type Progress struct {
ctx context.Context
uwg *sync.WaitGroup
cwg *sync.WaitGroup
bwg *sync.WaitGroup
operateState chan func(*pState)
interceptIo chan func(io.Writer)
done chan struct{}
refreshCh chan time.Time
once sync.Once
shutdown chan struct{}
cancel func()
}
// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine.
@ -37,20 +40,25 @@ type pState struct {
pMatrix map[int][]chan int
aMatrix map[int][]chan int
// for reuse purposes
rows []io.Reader
pool []*Bar
// following are provided/overrided by user
idCount int
reqWidth int
popPriority int
popCompleted bool
outputDiscarded bool
rr time.Duration
uwg *sync.WaitGroup
externalRefresh <-chan interface{}
renderDelay <-chan struct{}
shutdownNotifier chan struct{}
queueBars map[*Bar]*Bar
output io.Writer
debugOut io.Writer
refreshRate time.Duration
idCount int
reqWidth int
popPriority int
popCompleted bool
outputDiscarded bool
disableAutoRefresh bool
manualRefresh chan interface{}
renderDelay <-chan struct{}
shutdownNotifier chan struct{}
queueBars map[*Bar]*Bar
output io.Writer
debugOut io.Writer
uwg *sync.WaitGroup
}
// New creates new Progress container instance. It's not possible to
@ -64,11 +72,14 @@ func New(options ...ContainerOption) *Progress {
// method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
s := &pState{
bHeap: priorityQueue{},
rr: prr,
queueBars: make(map[*Bar]*Bar),
output: os.Stdout,
popPriority: math.MinInt32,
rows: make([]io.Reader, 0, 64),
pool: make([]*Bar, 0, 64),
refreshRate: defaultRefreshRate,
popPriority: math.MinInt32,
manualRefresh: make(chan interface{}),
queueBars: make(map[*Bar]*Bar),
output: os.Stdout,
debugOut: io.Discard,
}
for _, opt := range options {
@ -77,16 +88,24 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
}
}
ctx, cancel := context.WithCancel(ctx)
p := &Progress{
ctx: ctx,
uwg: s.uwg,
cwg: new(sync.WaitGroup),
bwg: new(sync.WaitGroup),
operateState: make(chan func(*pState)),
interceptIo: make(chan func(io.Writer)),
done: make(chan struct{}),
cancel: cancel,
}
if s.shutdownNotifier != nil {
p.shutdown = s.shutdownNotifier
s.shutdownNotifier = nil
} else {
p.shutdown = make(chan struct{})
}
p.cwg.Add(1)
go p.serve(s, cwriter.New(s.output))
return p
}
@ -101,15 +120,15 @@ func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
return p.New(total, SpinnerStyle(), options...)
}
// New creates a bar with provided BarFillerBuilder.
// New creates a bar by calling `Build` method on provided `BarFillerBuilder`.
func (p *Progress) New(total int64, builder BarFillerBuilder, options ...BarOption) *Bar {
return p.Add(total, builder.Build(), options...)
return p.AddFiller(total, builder.Build(), options...)
}
// Add creates a bar which renders itself by provided filler.
// AddFiller creates a bar which renders itself by provided filler.
// If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait().
func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar {
func (p *Progress) AddFiller(total int64, filler BarFiller, options ...BarOption) *Bar {
if filler == nil {
filler = NopStyle().Build()
}
@ -132,7 +151,7 @@ func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar
return bar
case <-p.done:
p.bwg.Done()
panic(fmt.Sprintf("%T instance can't be reused after it's done!", p))
panic(DoneError)
}
}
@ -140,13 +159,13 @@ func (p *Progress) traverseBars(cb func(b *Bar) bool) {
sync := make(chan struct{})
select {
case p.operateState <- func(s *pState) {
defer close(sync)
for i := 0; i < s.bHeap.Len(); i++ {
bar := s.bHeap[i]
if !cb(bar) {
break
}
}
close(sync)
}:
<-sync
case <-p.done:
@ -178,54 +197,113 @@ func (p *Progress) BarCount() int {
}
}
// Wait waits for all bars to complete and finally shutdowns container.
// After this method has been called, there is no way to reuse *Progress
// instance.
// Write is implementation of io.Writer.
// Writing to `*mpb.Progress` will print lines above a running bar.
// Writes aren't flushed immediately, but at next refresh cycle.
// If Write is called after `*mpb.Progress` is done, `mpb.DoneError`
// is returned.
func (p *Progress) Write(b []byte) (int, error) {
type result struct {
n int
err error
}
ch := make(chan *result)
select {
case p.interceptIo <- func(w io.Writer) {
n, err := w.Write(b)
ch <- &result{n, err}
}:
res := <-ch
return res.n, res.err
case <-p.done:
return 0, DoneError
}
}
// Wait waits for all bars to complete and finally shutdowns container. After
// this method has been called, there is no way to reuse (*Progress) instance.
func (p *Progress) Wait() {
// wait for user wg, if any
if p.uwg != nil {
p.uwg.Wait()
}
// wait for bars to quit, if any
p.bwg.Wait()
p.once.Do(p.shutdown)
// wait for container to quit
p.cwg.Wait()
p.Shutdown()
}
func (p *Progress) shutdown() {
close(p.done)
// Shutdown cancels any running bar immediately and then shutdowns (*Progress)
// instance. Normally this method shouldn't be called unless you know what you
// are doing. Proper way to shutdown is to call (*Progress).Wait() instead.
func (p *Progress) Shutdown() {
p.cancel()
<-p.shutdown
}
func (p *Progress) newTicker(s *pState) chan time.Time {
ch := make(chan time.Time)
go func() {
var autoRefresh <-chan time.Time
if !s.disableAutoRefresh && !s.outputDiscarded {
if s.renderDelay != nil {
<-s.renderDelay
}
ticker := time.NewTicker(s.refreshRate)
defer ticker.Stop()
autoRefresh = ticker.C
}
for {
select {
case t := <-autoRefresh:
ch <- t
case x := <-s.manualRefresh:
if t, ok := x.(time.Time); ok {
ch <- t
} else {
ch <- time.Now()
}
case <-p.ctx.Done():
close(p.done)
return
}
}
}()
return ch
}
func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
defer p.cwg.Done()
defer close(p.shutdown)
p.refreshCh = s.newTicker(p.done)
render := func(debugOut io.Writer) {
err := s.render(cw)
for err != nil {
if debugOut != nil {
_, err = fmt.Fprintln(debugOut, err)
} else {
panic(err)
}
debugOut = nil
render := func() error {
if s.bHeap.Len() == 0 {
return nil
}
return s.render(cw)
}
refreshCh := p.newTicker(s)
for {
select {
case op := <-p.operateState:
op(s)
case <-p.refreshCh:
render(s.debugOut)
case <-s.shutdownNotifier:
case fn := <-p.interceptIo:
fn(cw)
case <-refreshCh:
err := render()
if err != nil {
s.heapUpdated = false
render = func() error { return nil }
_, _ = fmt.Fprintln(s.debugOut, err.Error())
p.cancel() // cancel all bars
}
case <-p.done:
for s.heapUpdated {
render(s.debugOut)
err := render()
if err != nil {
_, _ = fmt.Fprintln(s.debugOut, err.Error())
return
}
}
return
}
@ -233,12 +311,13 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
}
func (s *pState) render(cw *cwriter.Writer) error {
var wg sync.WaitGroup
if s.heapUpdated {
s.updateSyncMatrix()
s.heapUpdated = false
}
syncWidth(s.pMatrix)
syncWidth(s.aMatrix)
syncWidth(&wg, s.pMatrix)
syncWidth(&wg, s.aMatrix)
width, height, err := cw.GetTermSize()
if err != nil {
@ -250,21 +329,25 @@ func (s *pState) render(cw *cwriter.Writer) error {
go bar.render(width)
}
return s.flush(cw, height)
err = s.flush(&wg, cw, height)
wg.Wait()
return err
}
func (s *pState) flush(cw *cwriter.Writer, height int) error {
var wg sync.WaitGroup
func (s *pState) flush(wg *sync.WaitGroup, cw *cwriter.Writer, height int) error {
var popCount int
rows := make([]io.Reader, 0, height)
pool := make([]*Bar, 0, s.bHeap.Len())
for s.bHeap.Len() > 0 {
var usedRows int
b := heap.Pop(&s.bHeap).(*Bar)
frame := <-b.frameCh
if frame.err != nil {
// b.frameCh is buffered it's ok to return here
return frame.err
}
var usedRows int
for i := len(frame.rows) - 1; i >= 0; i-- {
if row := frame.rows[i]; len(rows) < height {
rows = append(rows, row)
if row := frame.rows[i]; len(s.rows) < height {
s.rows = append(s.rows, row)
usedRows++
} else {
wg.Add(1)
@ -274,83 +357,65 @@ func (s *pState) flush(cw *cwriter.Writer, height int) error {
}()
}
}
if frame.shutdown != 0 {
if frame.shutdown {
b.Wait() // waiting for b.done, so it's safe to read b.bs
drop := b.bs.dropOnComplete
if qb, ok := s.queueBars[b]; ok {
delete(s.queueBars, b)
qb.priority = b.priority
pool = append(pool, qb)
drop = true
} else if s.popCompleted && !b.bs.noPop {
if frame.shutdown > 1 {
popCount += usedRows
drop = true
} else {
s.popPriority++
b.priority = s.popPriority
}
s.pool = append(s.pool, qb)
s.heapUpdated = true
continue
}
if drop {
if s.popCompleted && !b.bs.noPop {
switch b.bs.shutdown++; b.bs.shutdown {
case 1:
b.priority = s.popPriority
s.popPriority++
default:
if b.bs.dropOnComplete {
popCount += usedRows
s.heapUpdated = true
continue
}
}
} else if b.bs.dropOnComplete {
s.heapUpdated = true
continue
}
}
pool = append(pool, b)
s.pool = append(s.pool, b)
}
for _, b := range pool {
heap.Push(&s.bHeap, b)
switch len(s.pool) {
case 0:
if s.heapUpdated {
s.updateSyncMatrix()
s.heapUpdated = false
}
case 1:
heap.Push(&s.bHeap, s.pool[0])
s.pool = s.pool[:0]
default:
wg.Add(1)
go func() {
for _, b := range s.pool {
heap.Push(&s.bHeap, b)
}
s.pool = s.pool[:0]
wg.Done()
}()
}
for i := len(rows) - 1; i >= 0; i-- {
_, err := cw.ReadFrom(rows[i])
for i := len(s.rows) - 1; i >= 0; i-- {
_, err := cw.ReadFrom(s.rows[i])
if err != nil {
wg.Wait()
return err
}
}
wg.Wait()
return cw.Flush(len(rows) - popCount)
}
func (s *pState) newTicker(done <-chan struct{}) chan time.Time {
ch := make(chan time.Time)
if s.shutdownNotifier == nil {
s.shutdownNotifier = make(chan struct{})
}
go func() {
if s.renderDelay != nil {
<-s.renderDelay
}
var internalRefresh <-chan time.Time
if !s.outputDiscarded {
if s.externalRefresh == nil {
ticker := time.NewTicker(s.rr)
defer ticker.Stop()
internalRefresh = ticker.C
}
} else {
s.externalRefresh = nil
}
for {
select {
case t := <-internalRefresh:
ch <- t
case x := <-s.externalRefresh:
if t, ok := x.(time.Time); ok {
ch <- t
} else {
ch <- time.Now()
}
case <-done:
close(s.shutdownNotifier)
return
}
}
}()
return ch
err := cw.Flush(len(s.rows) - popCount)
s.rows = s.rows[:0]
return err
}
func (s *pState) updateSyncMatrix() {
@ -359,13 +424,12 @@ func (s *pState) updateSyncMatrix() {
for i := 0; i < s.bHeap.Len(); i++ {
bar := s.bHeap[i]
table := bar.wSyncTable()
pRow, aRow := table[0], table[1]
for i, ch := range pRow {
for i, ch := range table[0] {
s.pMatrix[i] = append(s.pMatrix[i], ch)
}
for i, ch := range aRow {
for i, ch := range table[1] {
s.aMatrix[i] = append(s.aMatrix[i], ch)
}
}
@ -373,12 +437,12 @@ func (s *pState) updateSyncMatrix() {
func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
bs := &bState{
id: s.idCount,
priority: s.idCount,
reqWidth: s.reqWidth,
total: total,
filler: filler,
debugOut: s.debugOut,
id: s.idCount,
priority: s.idCount,
reqWidth: s.reqWidth,
total: total,
filler: filler,
manualRefresh: s.manualRefresh,
}
if total > 0 {
@ -405,13 +469,14 @@ func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOptio
return bs
}
func syncWidth(matrix map[int][]chan int) {
func syncWidth(wg *sync.WaitGroup, matrix map[int][]chan int) {
for _, column := range matrix {
go maxWidthDistributor(column)
wg.Add(1)
go maxWidthDistributor(wg, column)
}
}
func maxWidthDistributor(column []chan int) {
func maxWidthDistributor(wg *sync.WaitGroup, column []chan int) {
var maxWidth int
for _, ch := range column {
if w := <-ch; w > maxWidth {
@ -421,4 +486,5 @@ func maxWidthDistributor(column []chan int) {
for _, ch := range column {
ch <- maxWidth
}
wg.Done()
}

96
vendor/github.com/vbauerster/mpb/v8/proxyreader.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
package mpb
import (
"io"
"time"
)
type proxyReader struct {
io.ReadCloser
bar *Bar
}
func (x proxyReader) Read(p []byte) (int, error) {
n, err := x.ReadCloser.Read(p)
x.bar.IncrBy(n)
return n, err
}
type proxyWriterTo struct {
proxyReader
}
func (x proxyWriterTo) WriteTo(w io.Writer) (int64, error) {
n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
x.bar.IncrInt64(n)
return n, err
}
type ewmaProxyReader struct {
io.ReadCloser
bar *Bar
}
func (x ewmaProxyReader) Read(p []byte) (int, error) {
start := time.Now()
n, err := x.ReadCloser.Read(p)
x.bar.EwmaIncrBy(n, time.Since(start))
return n, err
}
type ewmaProxyWriterTo struct {
ewmaProxyReader
}
func (x ewmaProxyWriterTo) WriteTo(w io.Writer) (int64, error) {
start := time.Now()
n, err := x.ReadCloser.(io.WriterTo).WriteTo(w)
x.bar.EwmaIncrInt64(n, time.Since(start))
return n, err
}
func newProxyReader(r io.Reader, b *Bar, hasEwma bool) io.ReadCloser {
rc := toReadCloser(r)
if hasEwma {
epr := ewmaProxyReader{rc, b}
if _, ok := r.(io.WriterTo); ok {
return ewmaProxyWriterTo{epr}
}
return epr
}
pr := proxyReader{rc, b}
if _, ok := r.(io.WriterTo); ok {
return proxyWriterTo{pr}
}
return pr
}
func toReadCloser(r io.Reader) io.ReadCloser {
if rc, ok := r.(io.ReadCloser); ok {
return rc
}
return toNopReadCloser(r)
}
func toNopReadCloser(r io.Reader) io.ReadCloser {
if _, ok := r.(io.WriterTo); ok {
return nopReadCloserWriterTo{r}
}
return nopReadCloser{r}
}
type nopReadCloser struct {
io.Reader
}
func (nopReadCloser) Close() error { return nil }
type nopReadCloserWriterTo struct {
io.Reader
}
func (nopReadCloserWriterTo) Close() error { return nil }
func (c nopReadCloserWriterTo) WriteTo(w io.Writer) (int64, error) {
return c.Reader.(io.WriterTo).WriteTo(w)
}

96
vendor/github.com/vbauerster/mpb/v8/proxywriter.go generated vendored Normal file
View file

@ -0,0 +1,96 @@
package mpb
import (
"io"
"time"
)
type proxyWriter struct {
io.WriteCloser
bar *Bar
}
func (x proxyWriter) Write(p []byte) (int, error) {
n, err := x.WriteCloser.Write(p)
x.bar.IncrBy(n)
return n, err
}
type proxyReaderFrom struct {
proxyWriter
}
func (x proxyReaderFrom) ReadFrom(r io.Reader) (int64, error) {
n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r)
x.bar.IncrInt64(n)
return n, err
}
type ewmaProxyWriter struct {
io.WriteCloser
bar *Bar
}
func (x ewmaProxyWriter) Write(p []byte) (int, error) {
start := time.Now()
n, err := x.WriteCloser.Write(p)
x.bar.EwmaIncrBy(n, time.Since(start))
return n, err
}
type ewmaProxyReaderFrom struct {
ewmaProxyWriter
}
func (x ewmaProxyReaderFrom) ReadFrom(r io.Reader) (int64, error) {
start := time.Now()
n, err := x.WriteCloser.(io.ReaderFrom).ReadFrom(r)
x.bar.EwmaIncrInt64(n, time.Since(start))
return n, err
}
func newProxyWriter(w io.Writer, b *Bar, hasEwma bool) io.WriteCloser {
wc := toWriteCloser(w)
if hasEwma {
epw := ewmaProxyWriter{wc, b}
if _, ok := w.(io.ReaderFrom); ok {
return ewmaProxyReaderFrom{epw}
}
return epw
}
pw := proxyWriter{wc, b}
if _, ok := w.(io.ReaderFrom); ok {
return proxyReaderFrom{pw}
}
return pw
}
func toWriteCloser(w io.Writer) io.WriteCloser {
if wc, ok := w.(io.WriteCloser); ok {
return wc
}
return toNopWriteCloser(w)
}
func toNopWriteCloser(w io.Writer) io.WriteCloser {
if _, ok := w.(io.ReaderFrom); ok {
return nopWriteCloserReaderFrom{w}
}
return nopWriteCloser{w}
}
type nopWriteCloser struct {
io.Writer
}
func (nopWriteCloser) Close() error { return nil }
type nopWriteCloserReaderFrom struct {
io.Writer
}
func (nopWriteCloserReaderFrom) Close() error { return nil }
func (c nopWriteCloserReaderFrom) ReadFrom(r io.Reader) (int64, error) {
return c.Writer.(io.ReaderFrom).ReadFrom(r)
}

16
vendor/modules.txt vendored
View file

@ -175,7 +175,7 @@ github.com/containers/common/version
# github.com/containers/conmon v2.0.20+incompatible
## explicit
github.com/containers/conmon/runner/config
# github.com/containers/image/v5 v5.24.0
# github.com/containers/image/v5 v5.24.1-0.20230202144111-a49c94a010be
## explicit; go 1.17
github.com/containers/image/v5/copy
github.com/containers/image/v5/directory
@ -838,7 +838,7 @@ github.com/syndtr/gocapability/capability
# github.com/tchap/go-patricia v2.3.0+incompatible
## explicit
github.com/tchap/go-patricia/patricia
# github.com/theupdateframework/go-tuf v0.5.2-0.20221207161717-9cb61d6e65f5
# github.com/theupdateframework/go-tuf v0.5.2
## explicit; go 1.18
github.com/theupdateframework/go-tuf/encrypted
# github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399
@ -863,12 +863,12 @@ github.com/ulikunitz/xz/lzma
github.com/vbatts/tar-split/archive/tar
github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage
# github.com/vbauerster/mpb/v7 v7.5.3
## explicit; go 1.14
github.com/vbauerster/mpb/v7
github.com/vbauerster/mpb/v7/cwriter
github.com/vbauerster/mpb/v7/decor
github.com/vbauerster/mpb/v7/internal
# github.com/vbauerster/mpb/v8 v8.1.4
## explicit; go 1.17
github.com/vbauerster/mpb/v8
github.com/vbauerster/mpb/v8/cwriter
github.com/vbauerster/mpb/v8/decor
github.com/vbauerster/mpb/v8/internal
# github.com/vishvananda/netlink v1.2.1-beta.2
## explicit; go 1.12
github.com/vishvananda/netlink