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/buildah v1.29.0
github.com/containers/common v0.51.0 github.com/containers/common v0.51.0
github.com/containers/conmon v2.0.20+incompatible 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/ocicrypt v1.1.7
github.com/containers/psgo v1.8.0 github.com/containers/psgo v1.8.0
github.com/containers/storage v1.45.3 github.com/containers/storage v1.45.3
@ -57,7 +57,7 @@ require (
github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635 github.com/syndtr/gocapability v0.0.0-20200815063812-42c35b437635
github.com/uber/jaeger-client-go v2.30.0+incompatible github.com/uber/jaeger-client-go v2.30.0+incompatible
github.com/ulikunitz/xz v0.5.11 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 github.com/vishvananda/netlink v1.2.1-beta.2
go.etcd.io/bbolt v1.3.7 go.etcd.io/bbolt v1.3.7
golang.org/x/net v0.5.0 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/stefanberger/go-pkcs11uri v0.0.0-20201008174630-78d3cae3a980 // indirect
github.com/sylabs/sif/v2 v2.9.0 // indirect github.com/sylabs/sif/v2 v2.9.0 // indirect
github.com/tchap/go-patricia v2.3.0+incompatible // 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/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 // indirect
github.com/vbatts/tar-split v0.11.2 // indirect github.com/vbatts/tar-split v0.11.2 // indirect
github.com/vishvananda/netns v0.0.0-20210104183010-2eb08e3e575f // 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/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 h1:YbCVSFSCqFjjVwHTPINGdMX1F6JXHGTUje2ZYobNrkg=
github.com/containers/conmon v2.0.20+incompatible/go.mod h1:hgwZ2mtuDrppv78a/cOBNiCm6O0UMWGx1mu7P00nu5I= 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.1-0.20230202144111-a49c94a010be h1:Bcsn9ohcVpCM9D2kZvIVMrO0QmmP87jhVZh/r9WrQ18=
github.com/containers/image/v5 v5.24.0/go.mod h1:oss5F6ssGQz8ZtC79oY+fuzYA3m3zBek9tq9gmhuvHc= 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 h1:Qzk5C6cYglewc+UyGf6lc8Mj2UaPTHy/iF2De0/77CA=
github.com/containers/libtrust v0.0.0-20230121012942-c1716e8a8d01/go.mod h1:9rfv8iPl1ZP7aqh9YA68wnZv2NUDbXdcdPHVz0pFbPY= 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= 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-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.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.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 h1:+xnbZSEeDbOIg5/mE6JF0w6n9duR1l3/WmbinWVwUuU=
github.com/mattn/go-runewidth v0.0.14/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w= 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= 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.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 h1:GkY4dP3cEfEASBPPkWd+AmjYxhmDkqO9/zg7R0lSQRs=
github.com/tchap/go-patricia v2.3.0+incompatible/go.mod h1:bmLyhP68RS6kStMGxByiQ23RP/odRBOTVjwp2cDyi6I= 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 h1:habfDzTmpbzBLIFGWa2ZpVhYvFBoK0C1onC3a4zuPRA=
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/go.mod h1:SyMV5kg5n4uEclsyxXJZI2UxPFJNDc4Y+r7wv+MlvTA=
github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk= github.com/tidwall/pretty v1.0.0/go.mod h1:XNkn88O1ChpSDQmQeStsy+sBenx6DDtFZJxhVysOjyk=
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs= 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= 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/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 h1:Via6XqJr0hceW4wff3QRzD5gAk/tatMw/4ZA7cTlIME=
github.com/vbatts/tar-split v0.11.2/go.mod h1:vV3ZuO2yWSVsz+pfFzDG/upWH1JhjOiEaWq6kXyQ3VI= 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/v8 v8.1.4 h1:MOcLTIbbAA892wVjRiuFHa1nRlNvifQMDVh12Bq/xIs=
github.com/vbauerster/mpb/v7 v7.5.3/go.mod h1:i+h4QY6lmLvBNK2ah1fSreiw3ajskRlBp9AhY/PnuOE= 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 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.0/go.mod h1:cTgwzPIzzgDAYoQrMm0EdrjRUBkTqKYppBueQtXaqoE=
github.com/vishvananda/netlink v1.1.1-0.20201029203352-d40f9887b852/go.mod h1:twkDnbuQxJYemMlGd4JFIcuhgX83tXhKS2B/PRMpOho= 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-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-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-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.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18= golang.org/x/sys v0.4.0 h1:Zr2JFtRQNX3BCZ8YtxRE9hNJYC8J6I1MVbMg6owUp18=
golang.org/x/sys v0.4.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= 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/containers/storage/pkg/archive"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"github.com/ulikunitz/xz" "github.com/ulikunitz/xz"
"github.com/vbauerster/mpb/v7" "github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v7/decor" "github.com/vbauerster/mpb/v8/decor"
) )
// GenericDownload is used when a user provides a URL // GenericDownload is used when a user provides a URL

View File

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

View File

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

View File

@ -13,9 +13,9 @@ import (
"github.com/containers/image/v5/internal/rootless" "github.com/containers/image/v5/internal/rootless"
"github.com/containers/image/v5/types" "github.com/containers/image/v5/types"
"github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/homedir"
"github.com/ghodss/yaml"
"github.com/opencontainers/go-digest" "github.com/opencontainers/go-digest"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"gopkg.in/yaml.v3"
) )
// systemRegistriesDirPath is the path to registries.d, used for locating lookaside Docker signature storage. // 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. // 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! // NOTE: Keep this in sync with docs/registries.d.md!
type registryConfiguration struct { 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*), // 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. // registryNamespace defines lookaside locations for a single namespace.
type registryNamespace struct { type registryNamespace struct {
Lookaside string `json:"lookaside"` // For reading, and if LookasideStaging is not present, for writing. Lookaside string `yaml:"lookaside"` // For reading, and if LookasideStaging is not present, for writing.
LookasideStaging string `json:"lookaside-staging"` // For writing only. LookasideStaging string `yaml:"lookaside-staging"` // For writing only.
SigStore string `json:"sigstore"` // For compatibility, deprecated in favor of Lookaside. SigStore string `yaml:"sigstore"` // For compatibility, deprecated in favor of Lookaside.
SigStoreStaging string `json:"sigstore-staging"` // For compatibility, deprecated in favor of LookasideStaging. SigStoreStaging string `yaml:"sigstore-staging"` // For compatibility, deprecated in favor of LookasideStaging.
UseSigstoreAttachments *bool `json:"use-sigstore-attachments,omitempty"` UseSigstoreAttachments *bool `yaml:"use-sigstore-attachments,omitempty"`
} }
// lookasideStorageBase is an "opaque" type representing a lookaside Docker signature storage. // lookasideStorageBase is an "opaque" type representing a lookaside Docker signature storage.

View File

@ -2,6 +2,10 @@ package manifest
import "fmt" 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 // 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) // 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 { 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) return fmt.Sprintf("unsupported image-specific operation on artifact with type %q", e.mimeType)
} }

View File

@ -3,7 +3,7 @@ package openshift
import ( import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/base64"
"errors" "errors"
"fmt" "fmt"
"net" "net"
@ -17,10 +17,10 @@ import (
"time" "time"
"github.com/containers/storage/pkg/homedir" "github.com/containers/storage/pkg/homedir"
"github.com/ghodss/yaml"
"github.com/imdario/mergo" "github.com/imdario/mergo"
"github.com/sirupsen/logrus" "github.com/sirupsen/logrus"
"golang.org/x/net/http2" "golang.org/x/net/http2"
"gopkg.in/yaml.v3"
) )
// restTLSClientConfig is a modified copy of k8s.io/kubernetes/pkg/client/restclient.TLSClientConfig. // 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 return config, nil
} }
// Note: This does absolutely no kind/version checking or conversions. // Note: This does absolutely no kind/version checking or conversions.
data, err := yaml.YAMLToJSON(data) if err := yaml.Unmarshal(data, config); err != nil {
if err != nil {
return nil, err
}
if err := json.Unmarshal(data, config); err != nil {
return nil, err return nil, err
} }
return config, nil return config, nil
@ -1057,20 +1053,20 @@ func (c *restConfig) HasCertAuth() bool {
// IMPORTANT if you add fields to this struct, please update IsConfigEmpty() // IMPORTANT if you add fields to this struct, please update IsConfigEmpty()
type clientcmdConfig struct { type clientcmdConfig struct {
// Clusters is a map of referenceable names to cluster configs // 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 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 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 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 type clustersMap map[string]*clientcmdCluster
func (m *clustersMap) UnmarshalJSON(data []byte) error { func (m *clustersMap) UnmarshalYAML(value *yaml.Node) error {
var a []v1NamedCluster var a []v1NamedCluster
if err := json.Unmarshal(data, &a); err != nil { if err := value.Decode(&a); err != nil {
return err return err
} }
for _, e := range a { for _, e := range a {
@ -1082,9 +1078,9 @@ func (m *clustersMap) UnmarshalJSON(data []byte) error {
type authInfosMap map[string]*clientcmdAuthInfo type authInfosMap map[string]*clientcmdAuthInfo
func (m *authInfosMap) UnmarshalJSON(data []byte) error { func (m *authInfosMap) UnmarshalYAML(value *yaml.Node) error {
var a []v1NamedAuthInfo var a []v1NamedAuthInfo
if err := json.Unmarshal(data, &a); err != nil { if err := value.Decode(&a); err != nil {
return err return err
} }
for _, e := range a { for _, e := range a {
@ -1096,9 +1092,9 @@ func (m *authInfosMap) UnmarshalJSON(data []byte) error {
type contextsMap map[string]*clientcmdContext type contextsMap map[string]*clientcmdContext
func (m *contextsMap) UnmarshalJSON(data []byte) error { func (m *contextsMap) UnmarshalYAML(value *yaml.Node) error {
var a []v1NamedContext var a []v1NamedContext
if err := json.Unmarshal(data, &a); err != nil { if err := value.Decode(&a); err != nil {
return err return err
} }
for _, e := range a { 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. // 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 // Cluster contains information about how to communicate with a kubernetes cluster
type clientcmdCluster struct { type clientcmdCluster struct {
// LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized. // LocationOfOrigin indicates where this object came from. It is used for round tripping config post-merge, but never serialized.
LocationOfOrigin string LocationOfOrigin string
// Server is the address of the kubernetes cluster (https://hostname:port). // 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 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 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 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. // 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 indicates where this object came from. It is used for round tripping config post-merge, but never serialized.
LocationOfOrigin string LocationOfOrigin string
// ClientCertificate is the path to a client cert file for TLS. // 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 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 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 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 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 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 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. // 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 indicates where this object came from. It is used for round tripping config post-merge, but never serialized.
LocationOfOrigin string LocationOfOrigin string
// Cluster is the name of the cluster for this context // 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 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 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. // v1NamedCluster is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.v1.NamedCluster.
// NamedCluster relates nicknames to cluster information // NamedCluster relates nicknames to cluster information
type v1NamedCluster struct { type v1NamedCluster struct {
// Name is the nickname for this Cluster // Name is the nickname for this Cluster
Name string `json:"name"` Name string `yaml:"name"`
// Cluster holds the cluster information // 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. // v1NamedContext is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.v1.NamedContext.
// NamedContext relates nicknames to context information // NamedContext relates nicknames to context information
type v1NamedContext struct { type v1NamedContext struct {
// Name is the nickname for this Context // Name is the nickname for this Context
Name string `json:"name"` Name string `yaml:"name"`
// Context holds the context information // 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. // v1NamedAuthInfo is a modified copy of k8s.io/kubernetes/pkg/client/unversioned/clientcmd/api.v1.NamedAuthInfo.
// NamedAuthInfo relates nicknames to auth information // NamedAuthInfo relates nicknames to auth information
type v1NamedAuthInfo struct { type v1NamedAuthInfo struct {
// Name is the nickname for this AuthInfo // Name is the nickname for this AuthInfo
Name string `json:"name"` Name string `yaml:"name"`
// AuthInfo holds the auth information // 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 is for functionality in a backwards-compatible manner
VersionMinor = 24 VersionMinor = 24
// VersionPatch is for backwards-compatible bug fixes // VersionPatch is for backwards-compatible bug fixes
VersionPatch = 0 VersionPatch = 1
// VersionDev indicates development branch. Releases will be empty string. // VersionDev indicates development branch. Releases will be empty string.
VersionDev = "" VersionDev = "-dev"
) )
// Version is the specification version that the package types support. // 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 # 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) [![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. **mpb** is a Go lib for rendering progress bars in terminal applications.
@ -26,8 +26,8 @@ import (
"math/rand" "math/rand"
"time" "time"
"github.com/vbauerster/mpb/v7" "github.com/vbauerster/mpb/v8"
"github.com/vbauerster/mpb/v7/decor" "github.com/vbauerster/mpb/v8/decor"
) )
func main() { func main() {
@ -97,9 +97,8 @@ func main() {
// EWMA's unit of measure is an iteration's duration // EWMA's unit of measure is an iteration's duration
start := time.Now() start := time.Now()
time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10) time.Sleep(time.Duration(rng.Intn(10)+1) * max / 10)
bar.Increment() // we need to call EwmaIncrement to fulfill ewma decorator's contract
// we need to call DecoratorEwmaUpdate to fulfill ewma decorator's contract bar.EwmaIncrement(time.Since(start))
bar.DecoratorEwmaUpdate(time.Since(start))
} }
}() }()
} }

View File

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

View File

@ -3,17 +3,13 @@ package mpb
import ( import (
"io" "io"
"github.com/vbauerster/mpb/v7/decor" "github.com/vbauerster/mpb/v8/decor"
) )
// BarFiller interface. // BarFiller interface.
// Bar (without decorators) renders itself by calling BarFiller's Fill method. // 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 { type BarFiller interface {
Fill(w io.Writer, reqWidth int, stat decor.Statistics) Fill(io.Writer, decor.Statistics) error
} }
// BarFillerBuilder interface. // BarFillerBuilder interface.
@ -22,17 +18,16 @@ type BarFiller interface {
// BarStyle() // BarStyle()
// SpinnerStyle() // SpinnerStyle()
// NopStyle() // NopStyle()
//
type BarFillerBuilder interface { type BarFillerBuilder interface {
Build() BarFiller Build() BarFiller
} }
// BarFillerFunc is function type adapter to convert compatible function // BarFillerFunc is function type adapter to convert compatible function
// into BarFiller interface. // 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) { func (f BarFillerFunc) Fill(w io.Writer, stat decor.Statistics) error {
f(w, reqWidth, stat) return f(w, stat)
} }
// BarFillerBuilderFunc is function type adapter to convert compatible // BarFillerBuilderFunc is function type adapter to convert compatible
@ -42,9 +37,3 @@ type BarFillerBuilderFunc func() BarFiller
func (f BarFillerBuilderFunc) Build() BarFiller { func (f BarFillerBuilderFunc) Build() BarFiller {
return f() 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/acarl005/stripansi"
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v7/decor" "github.com/vbauerster/mpb/v8/decor"
"github.com/vbauerster/mpb/v7/internal" "github.com/vbauerster/mpb/v8/internal"
) )
const ( const (
@ -36,8 +36,8 @@ type bFiller struct {
components [components]*component components [components]*component
tip struct { tip struct {
count uint count uint
onComplete *component
frames []*component frames []*component
onComplete *component
} }
} }
@ -148,20 +148,22 @@ func (s *barStyle) Build() BarFiller {
return bf return bf
} }
func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) { func (s *bFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
width = internal.CheckRequestedWidth(width, stat.AvailableWidth) width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
brackets := s.components[iLbound].width + s.components[iRbound].width
// don't count brackets as progress // don't count brackets as progress
width -= brackets width -= (s.components[iLbound].width + s.components[iRbound].width)
if width < 0 { if width < 0 {
return return nil
} }
mustWrite(w, s.components[iLbound].bytes) _, err = w.Write(s.components[iLbound].bytes)
defer mustWrite(w, s.components[iRbound].bytes) if err != nil {
return err
}
if width == 0 { if width == 0 {
return _, err = w.Write(s.components[iRbound].bytes)
return err
} }
var filling [][]byte var filling [][]byte
@ -171,7 +173,7 @@ func (s *bFiller) Fill(w io.Writer, width int, stat decor.Statistics) {
var refWidth int var refWidth int
curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width))) curWidth := int(internal.PercentageRound(stat.Total, stat.Current, uint(width)))
if stat.Current >= stat.Total { if stat.Completed {
tip = s.tip.onComplete tip = s.tip.onComplete
} else { } else {
tip = s.tip.frames[s.tip.count%uint(len(s.tip.frames))] 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 { if s.rev {
flush(w, padding, filling) filling, padding = padding, filling
} else {
flush(w, filling, padding)
} }
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-- { 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++ { for i := 0; i < len(padding); i++ {
mustWrite(w, padding[i]) _, err := w.Write(padding[i])
} if err != nil {
} return err
}
func mustWrite(w io.Writer, p []byte) {
_, err := w.Write(p)
if err != nil {
panic(err)
} }
return nil
} }

View File

@ -3,12 +3,14 @@ package mpb
import ( import (
"io" "io"
"github.com/vbauerster/mpb/v7/decor" "github.com/vbauerster/mpb/v8/decor"
) )
// NopStyle provides BarFillerBuilder which builds NOP BarFiller. // NopStyle provides BarFillerBuilder which builds NOP BarFiller.
func NopStyle() BarFillerBuilder { func NopStyle() BarFillerBuilder {
return BarFillerBuilderFunc(func() BarFiller { 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/acarl005/stripansi"
"github.com/mattn/go-runewidth" "github.com/mattn/go-runewidth"
"github.com/vbauerster/mpb/v7/decor" "github.com/vbauerster/mpb/v8/decor"
"github.com/vbauerster/mpb/v7/internal" "github.com/vbauerster/mpb/v8/internal"
) )
const ( const (
@ -63,17 +63,16 @@ func (s *spinnerStyle) Build() BarFiller {
return sf return sf
} }
func (s *sFiller) Fill(w io.Writer, width int, stat decor.Statistics) { func (s *sFiller) Fill(w io.Writer, stat decor.Statistics) (err error) {
width = internal.CheckRequestedWidth(width, stat.AvailableWidth) width := internal.CheckRequestedWidth(stat.RequestedWidth, stat.AvailableWidth)
frame := s.frames[s.count%uint(len(s.frames))] frame := s.frames[s.count%uint(len(s.frames))]
frameWidth := runewidth.StringWidth(stripansi.Strip(frame)) frameWidth := runewidth.StringWidth(stripansi.Strip(frame))
if width < frameWidth { if width < frameWidth {
return return nil
} }
var err error
rest := width - frameWidth rest := width - frameWidth
switch s.position { switch s.position {
case positionLeft: 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) str := strings.Repeat(" ", rest/2) + frame + strings.Repeat(" ", rest/2+rest%2)
_, err = io.WriteString(w, str) _, err = io.WriteString(w, str)
} }
if err != nil {
panic(err)
}
s.count++ s.count++
return err
} }

View File

@ -4,44 +4,41 @@ import (
"bytes" "bytes"
"io" "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. // BarOption is a func option to alter default behavior of a bar.
type BarOption func(*bState) type BarOption func(*bState)
func skipNil(decorators []decor.Decorator) (filtered []decor.Decorator) { func inspect(decorators []decor.Decorator) (dest []decor.Decorator) {
for _, d := range decorators {
if d != nil {
filtered = append(filtered, d)
}
}
return
}
func (s *bState) addDecorators(dest *[]decor.Decorator, decorators ...decor.Decorator) {
type mergeWrapper interface { type mergeWrapper interface {
MergeUnwrap() []decor.Decorator MergeUnwrap() []decor.Decorator
} }
for _, decorator := range decorators { for _, decorator := range decorators {
if mw, ok := decorator.(mergeWrapper); ok { if decorator == nil {
*dest = append(*dest, mw.MergeUnwrap()...) 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. // AppendDecorators let you inject decorators to the bar's right side.
func AppendDecorators(decorators ...decor.Decorator) BarOption { func AppendDecorators(decorators ...decor.Decorator) BarOption {
decorators = inspect(decorators)
return func(s *bState) { return func(s *bState) {
s.addDecorators(&s.aDecorators, skipNil(decorators)...) s.aDecorators = decorators
} }
} }
// PrependDecorators let you inject decorators to the bar's left side. // PrependDecorators let you inject decorators to the bar's left side.
func PrependDecorators(decorators ...decor.Decorator) BarOption { func PrependDecorators(decorators ...decor.Decorator) BarOption {
decorators = inspect(decorators)
return func(s *bState) { 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. // BarFillerOnComplete replaces bar's filler with message, on complete event.
func BarFillerOnComplete(message string) BarOption { func BarFillerOnComplete(message string) BarOption {
return BarFillerMiddleware(func(base BarFiller) BarFiller { 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 { if st.Completed {
_, err := io.WriteString(w, message) _, err := io.WriteString(w, message)
if err != nil { return err
panic(err)
}
} else {
base.Fill(w, reqWidth, st)
} }
return base.Fill(w, st)
}) })
}) })
} }
@ -121,32 +115,26 @@ func BarPriority(priority int) BarOption {
// BarExtender extends bar with arbitrary lines. Provided BarFiller will be // BarExtender extends bar with arbitrary lines. Provided BarFiller will be
// called at each render/flush cycle. Any lines written to the underlying // called at each render/flush cycle. Any lines written to the underlying
// io.Writer will be printed after the bar itself. // io.Writer will extend the bar either in above (rev = true) or below
func BarExtender(filler BarFiller) BarOption { // (rev = false) direction.
return barExtender(filler, false) func BarExtender(filler BarFiller, rev bool) BarOption {
}
// 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 {
if filler == nil { if filler == nil {
return nil return nil
} }
fn := makeExtenderFunc(filler, rev)
return func(s *bState) { return func(s *bState) {
s.extender = makeExtenderFunc(filler, rev) s.extender = fn
} }
} }
func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc { func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
buf := new(bytes.Buffer) buf := new(bytes.Buffer)
base := func(rows []io.Reader, width int, stat decor.Statistics) []io.Reader { base := func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
buf.Reset() err := filler.Fill(buf, stat)
filler.Fill(buf, width, stat) if err != nil {
buf.Reset()
return rows, err
}
for { for {
b, err := buf.ReadBytes('\n') b, err := buf.ReadBytes('\n')
if err != nil { if err != nil {
@ -154,19 +142,22 @@ func makeExtenderFunc(filler BarFiller, rev bool) extenderFunc {
} }
rows = append(rows, bytes.NewReader(b)) rows = append(rows, bytes.NewReader(b))
} }
return rows buf.Reset()
return rows, err
} }
if !rev { if !rev {
return base return base
} else { }
return func(rows []io.Reader, width int, stat decor.Statistics) []io.Reader { return func(rows []io.Reader, stat decor.Statistics) ([]io.Reader, error) {
rows = base(rows, width, stat) rows, err := base(rows, stat)
for left, right := 0, len(rows)-1; left < right; left, right = left+1, right-1 { if err != nil {
rows[left], rows[right] = rows[right], rows[left] return rows, err
}
return rows
} }
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 { func BarOptional(option BarOption, cond bool) BarOption {
if cond { if cond {
return option return option
@ -193,11 +184,26 @@ func BarOptional(option BarOption, cond bool) BarOption {
return nil return nil
} }
// BarOptOn will invoke provided option only when higher order predicate // BarOptOn will return provided option only when predicate evaluates to true.
// evaluates to true.
func BarOptOn(option BarOption, predicate func() bool) BarOption { func BarOptOn(option BarOption, predicate func() bool) BarOption {
if predicate() { if predicate() {
return option return option
} }
return nil 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. // WithRefreshRate overrides default 150ms refresh rate.
func WithRefreshRate(d time.Duration) ContainerOption { func WithRefreshRate(d time.Duration) ContainerOption {
return func(s *pState) { return func(s *pState) {
s.rr = d s.refreshRate = d
} }
} }
// WithManualRefresh disables internal auto refresh time.Ticker. // WithManualRefresh disables internal auto refresh time.Ticker.
// Refresh will occur upon receive value from provided ch. // 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) { 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, // will effectively disable auto refresh rate and discard any output,
// useful if you want to disable progress bars with little overhead. // useful if you want to disable progress bars with little overhead.
func WithOutput(w io.Writer) ContainerOption { func WithOutput(w io.Writer) ContainerOption {
var discarded bool
if w == nil {
w = io.Discard
discarded = true
}
return func(s *pState) { return func(s *pState) {
if w == nil {
s.output = io.Discard
s.outputDiscarded = true
return
}
s.output = w s.output = w
s.outputDiscarded = discarded
} }
} }
// WithDebugOutput sets debug output. // WithDebugOutput sets debug output.
func WithDebugOutput(w io.Writer) ContainerOption { func WithDebugOutput(w io.Writer) ContainerOption {
if w == nil { if w == nil {
return nil w = io.Discard
} }
return func(s *pState) { return func(s *pState) {
s.debugOut = w 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 { func PopCompletedMode() ContainerOption {
return func(s *pState) { return func(s *pState) {
s.popCompleted = true 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 { func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
if cond { if cond {
return option return option
@ -106,11 +110,26 @@ func ContainerOptional(option ContainerOption, cond bool) ContainerOption {
return nil return nil
} }
// ContainerOptOn will invoke provided option only when higher order // ContainerOptOn will return provided option only when predicate evaluates to true.
// predicate evaluates to true.
func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption { func ContainerOptOn(option ContainerOption, predicate func() bool) ContainerOption {
if predicate() { if predicate() {
return option return option
} }
return nil 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 package cwriter

View File

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

View File

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

View File

@ -1,4 +1,4 @@
// +build zos //go:build zos
package cwriter 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 package cwriter
import ( import (
"bytes"
"io"
"unsafe" "unsafe"
"golang.org/x/sys/windows" "golang.org/x/sys/windows"
@ -15,10 +17,37 @@ var (
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW") 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 { if !w.terminal {
// hope it's cygwin or similar // hope it's cygwin or similar
return w.ansiCuuAndEd() return w.ew.ansiCuuAndEd(w.out, n)
} }
var info windows.ConsoleScreenBufferInfo var info windows.ConsoleScreenBufferInfo
@ -26,7 +55,7 @@ func (w *Writer) clearLines() error {
return err return err
} }
info.CursorPosition.Y -= int16(w.lines) info.CursorPosition.Y -= int16(n)
if info.CursorPosition.Y < 0 { if info.CursorPosition.Y < 0 {
info.CursorPosition.Y = 0 info.CursorPosition.Y = 0
} }
@ -40,7 +69,7 @@ func (w *Writer) clearLines() error {
X: info.Window.Left, X: info.Window.Left,
Y: info.CursorPosition.Y, Y: info.CursorPosition.Y,
} }
count := uint32(info.Size.X) * uint32(w.lines) count := uint32(info.Size.X) * uint32(n)
_, _, _ = procFillConsoleOutputCharacter.Call( _, _, _ = procFillConsoleOutputCharacter.Call(
uintptr(w.fd), uintptr(w.fd),
uintptr(' '), uintptr(' '),
@ -52,7 +81,6 @@ func (w *Writer) clearLines() error {
} }
// GetSize returns the visible dimensions of the given terminal. // GetSize returns the visible dimensions of the given terminal.
//
// These dimensions don't include any scrollback buffer height. // These dimensions don't include any scrollback buffer height.
func GetSize(fd int) (width, height int, err error) { func GetSize(fd int) (width, height int, err error) {
var info windows.ConsoleScreenBufferInfo var info windows.ConsoleScreenBufferInfo

View File

@ -6,7 +6,6 @@ package decor
// `fn` DecorFunc callback // `fn` DecorFunc callback
// //
// `wcc` optional WC config // `wcc` optional WC config
//
func Any(fn DecorFunc, wcc ...WC) Decorator { func Any(fn DecorFunc, wcc ...WC) Decorator {
return &any{initWC(wcc...), fn} 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="% .1f / % .1f" output: "1.0 MB / 12.0 MB"
// pairFmt="%d / %d" output: "1MB / 12MB" // pairFmt="%d / %d" output: "1MB / 12MB"
// pairFmt="% d / % d" output: "1 MB / 12 MB" // pairFmt="% d / % d" output: "1 MB / 12 MB"
//
func Counters(unit int, pairFmt string, wcc ...WC) Decorator { func Counters(unit int, pairFmt string, wcc ...WC) Decorator {
producer := func(unit int, pairFmt string) DecorFunc { producer := func(unit int, pairFmt string) DecorFunc {
if pairFmt == "" { if pairFmt == "" {
@ -99,7 +98,6 @@ func TotalKiloByte(format string, wcc ...WC) Decorator {
// format="% .1f" output: "12.0 MiB" // format="% .1f" output: "12.0 MiB"
// format="%d" output: "12MiB" // format="%d" output: "12MiB"
// format="% d" output: "12 MiB" // format="% d" output: "12 MiB"
//
func Total(unit int, format string, wcc ...WC) Decorator { func Total(unit int, format string, wcc ...WC) Decorator {
producer := func(unit int, format string) DecorFunc { producer := func(unit int, format string) DecorFunc {
if format == "" { if format == "" {
@ -157,7 +155,6 @@ func CurrentKiloByte(format string, wcc ...WC) Decorator {
// format="% .1f" output: "12.0 MiB" // format="% .1f" output: "12.0 MiB"
// format="%d" output: "12MiB" // format="%d" output: "12MiB"
// format="% d" output: "12 MiB" // format="% d" output: "12 MiB"
//
func Current(unit int, format string, wcc ...WC) Decorator { func Current(unit int, format string, wcc ...WC) Decorator {
producer := func(unit int, format string) DecorFunc { producer := func(unit int, format string) DecorFunc {
if format == "" { if format == "" {
@ -215,7 +212,6 @@ func InvertedCurrentKiloByte(format string, wcc ...WC) Decorator {
// format="% .1f" output: "12.0 MiB" // format="% .1f" output: "12.0 MiB"
// format="%d" output: "12MiB" // format="%d" output: "12MiB"
// format="% d" output: "12 MiB" // format="% d" output: "12 MiB"
//
func InvertedCurrent(unit int, format string, wcc ...WC) Decorator { func InvertedCurrent(unit int, format string, wcc ...WC) Decorator {
producer := func(unit int, format string) DecorFunc { producer := func(unit int, format string) DecorFunc {
if format == "" { if format == "" {

View File

@ -44,10 +44,11 @@ const (
ET_STYLE_MMSS ET_STYLE_MMSS
) )
// Statistics consists of progress related statistics, that Decorator // Statistics contains fields which are necessary for implementing
// may need. // `decor.Decorator` and `mpb.BarFiller` interfaces.
type Statistics struct { type Statistics struct {
AvailableWidth int AvailableWidth int // calculated width initially equal to terminal width
RequestedWidth int // width set by `mpb.WithWidth`
ID int ID int
Total int64 Total int64
Current int64 Current int64
@ -138,17 +139,17 @@ type WC struct {
// Should be called by any Decorator implementation. // Should be called by any Decorator implementation.
func (wc *WC) FormatMsg(msg string) string { func (wc *WC) FormatMsg(msg string) string {
pureWidth := runewidth.StringWidth(msg) pureWidth := runewidth.StringWidth(msg)
stripWidth := runewidth.StringWidth(stripansi.Strip(msg)) viewWidth := runewidth.StringWidth(stripansi.Strip(msg))
maxCell := wc.W max := wc.W
if (wc.C & DSyncWidth) != 0 { if (wc.C & DSyncWidth) != 0 {
cellCount := stripWidth viewWidth := viewWidth
if (wc.C & DextraSpace) != 0 { if (wc.C & DextraSpace) != 0 {
cellCount++ viewWidth++
} }
wc.wsync <- cellCount wc.wsync <- viewWidth
maxCell = <-wc.wsync max = <-wc.wsync
} }
return wc.fill(msg, maxCell+(pureWidth-stripWidth)) return wc.fill(msg, max-viewWidth+pureWidth)
} }
// Init initializes width related config. // 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 // 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 // decorators concurrently, unless you share the same decorator among multiple
@ -6,10 +6,10 @@
// //
// Don't: // Don't:
// //
// p := mpb.New() // p := mpb.New()
// name := decor.Name("bar") // name := decor.Name("bar")
// p.AddBar(100, mpb.AppendDecorators(name)) // p.AddBar(100, mpb.AppendDecorators(name))
// p.AddBar(100, mpb.AppendDecorators(name)) // p.AddBar(100, mpb.AppendDecorators(name))
// //
// Do: // Do:
// //

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -4,7 +4,7 @@ import (
"fmt" "fmt"
"strconv" "strconv"
"github.com/vbauerster/mpb/v7/internal" "github.com/vbauerster/mpb/v8/internal"
) )
type percentageType float64 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(' ') { 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. // 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="% .1f" output: "1.0 %"
// format="%d" output: "1%" // format="%d" output: "1%"
// format="% d" output: "1 %" // format="% d" output: "1 %"
//
func NewPercentage(format string, wcc ...WC) Decorator { func NewPercentage(format string, wcc ...WC) Decorator {
if format == "" { if format == "" {
format = "% d" 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 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(' ') { 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 ( const (
@ -97,9 +103,15 @@ func (self SizeB1000) Format(st fmt.State, verb rune) {
unit = _TB 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(' ') { 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: // used with SizeB1000 or SizeB1024 types, for example:
// //
// fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048))) // fmt.Printf("%.1f", FmtAsSpeed(SizeB1024(2048)))
//
func FmtAsSpeed(input fmt.Formatter) fmt.Formatter { func FmtAsSpeed(input fmt.Formatter) fmt.Formatter {
return &speedFormatter{input} return &speedFormatter{input}
} }
@ -23,13 +22,15 @@ type speedFormatter struct {
func (self *speedFormatter) Format(st fmt.State, verb rune) { func (self *speedFormatter) Format(st fmt.State, verb rune) {
self.Formatter.Format(st, verb) 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. // EwmaSpeed exponential-weighted-moving-average based speed decorator.
// For this decorator to work correctly you have to measure each // For this decorator to work correctly you have to measure each iteration's
// iteration's duration and pass it to the // duration and pass it to one of the (*Bar).EwmaIncr... family methods.
// *Bar.DecoratorEwmaUpdate(time.Duration) method after each increment.
func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator { func EwmaSpeed(unit int, format string, age float64, wcc ...WC) Decorator {
var average ewma.MovingAverage var average ewma.MovingAverage
if age == 0 { 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=UnitKiB, format="% .1f" output: "1.0 MiB/s"
// unit=UnitKB, format="%.1f" output: "1.0MB/s" // unit=UnitKB, format="%.1f" output: "1.0MB/s"
// unit=UnitKB, format="% .1f" output: "1.0 MB/s" // unit=UnitKB, format="% .1f" output: "1.0 MB/s"
//
func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator { func MovingAverageSpeed(unit int, format string, average ewma.MovingAverage, wcc ...WC) Decorator {
if format == "" { if format == "" {
format = "%.0f" 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=UnitKiB, format="% .1f" output: "1.0 MiB/s"
// unit=UnitKB, format="%.1f" output: "1.0MB/s" // unit=UnitKB, format="%.1f" output: "1.0MB/s"
// unit=UnitKB, format="% .1f" output: "1.0 MB/s" // unit=UnitKB, format="% .1f" output: "1.0 MB/s"
//
func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator { func NewAverageSpeed(unit int, format string, startTime time.Time, wcc ...WC) Decorator {
if format == "" { if format == "" {
format = "%.0f" format = "%.0f"

View File

@ -11,23 +11,26 @@ import (
"sync" "sync"
"time" "time"
"github.com/vbauerster/mpb/v7/cwriter" "github.com/vbauerster/mpb/v8/cwriter"
) )
const ( 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. // Progress represents a container that renders one or more progress bars.
type Progress struct { type Progress struct {
ctx context.Context ctx context.Context
uwg *sync.WaitGroup uwg *sync.WaitGroup
cwg *sync.WaitGroup
bwg *sync.WaitGroup bwg *sync.WaitGroup
operateState chan func(*pState) operateState chan func(*pState)
interceptIo chan func(io.Writer)
done chan struct{} done chan struct{}
refreshCh chan time.Time shutdown chan struct{}
once sync.Once cancel func()
} }
// pState holds bars in its priorityQueue, it gets passed to (*Progress).serve monitor goroutine. // 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 pMatrix map[int][]chan int
aMatrix map[int][]chan int aMatrix map[int][]chan int
// for reuse purposes
rows []io.Reader
pool []*Bar
// following are provided/overrided by user // following are provided/overrided by user
idCount int refreshRate time.Duration
reqWidth int idCount int
popPriority int reqWidth int
popCompleted bool popPriority int
outputDiscarded bool popCompleted bool
rr time.Duration outputDiscarded bool
uwg *sync.WaitGroup disableAutoRefresh bool
externalRefresh <-chan interface{} manualRefresh chan interface{}
renderDelay <-chan struct{} renderDelay <-chan struct{}
shutdownNotifier chan struct{} shutdownNotifier chan struct{}
queueBars map[*Bar]*Bar queueBars map[*Bar]*Bar
output io.Writer output io.Writer
debugOut io.Writer debugOut io.Writer
uwg *sync.WaitGroup
} }
// New creates new Progress container instance. It's not possible to // New creates new Progress container instance. It's not possible to
@ -64,11 +72,14 @@ func New(options ...ContainerOption) *Progress {
// method has been called. // method has been called.
func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress { func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
s := &pState{ s := &pState{
bHeap: priorityQueue{}, rows: make([]io.Reader, 0, 64),
rr: prr, pool: make([]*Bar, 0, 64),
queueBars: make(map[*Bar]*Bar), refreshRate: defaultRefreshRate,
output: os.Stdout, popPriority: math.MinInt32,
popPriority: math.MinInt32, manualRefresh: make(chan interface{}),
queueBars: make(map[*Bar]*Bar),
output: os.Stdout,
debugOut: io.Discard,
} }
for _, opt := range options { for _, opt := range options {
@ -77,16 +88,24 @@ func NewWithContext(ctx context.Context, options ...ContainerOption) *Progress {
} }
} }
ctx, cancel := context.WithCancel(ctx)
p := &Progress{ p := &Progress{
ctx: ctx, ctx: ctx,
uwg: s.uwg, uwg: s.uwg,
cwg: new(sync.WaitGroup),
bwg: new(sync.WaitGroup), bwg: new(sync.WaitGroup),
operateState: make(chan func(*pState)), operateState: make(chan func(*pState)),
interceptIo: make(chan func(io.Writer)),
done: make(chan struct{}), 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)) go p.serve(s, cwriter.New(s.output))
return p return p
} }
@ -101,15 +120,15 @@ func (p *Progress) AddSpinner(total int64, options ...BarOption) *Bar {
return p.New(total, SpinnerStyle(), options...) 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 { 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. // If `total <= 0` triggering complete event by increment methods is disabled.
// Panics if *Progress instance is done, i.e. called after (*Progress).Wait(). // 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 { if filler == nil {
filler = NopStyle().Build() filler = NopStyle().Build()
} }
@ -132,7 +151,7 @@ func (p *Progress) Add(total int64, filler BarFiller, options ...BarOption) *Bar
return bar return bar
case <-p.done: case <-p.done:
p.bwg.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{}) sync := make(chan struct{})
select { select {
case p.operateState <- func(s *pState) { case p.operateState <- func(s *pState) {
defer close(sync)
for i := 0; i < s.bHeap.Len(); i++ { for i := 0; i < s.bHeap.Len(); i++ {
bar := s.bHeap[i] bar := s.bHeap[i]
if !cb(bar) { if !cb(bar) {
break break
} }
} }
close(sync)
}: }:
<-sync <-sync
case <-p.done: case <-p.done:
@ -178,54 +197,113 @@ func (p *Progress) BarCount() int {
} }
} }
// Wait waits for all bars to complete and finally shutdowns container. // Write is implementation of io.Writer.
// After this method has been called, there is no way to reuse *Progress // Writing to `*mpb.Progress` will print lines above a running bar.
// instance. // 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() { func (p *Progress) Wait() {
// wait for user wg, if any // wait for user wg, if any
if p.uwg != nil { if p.uwg != nil {
p.uwg.Wait() p.uwg.Wait()
} }
// wait for bars to quit, if any
p.bwg.Wait() p.bwg.Wait()
p.Shutdown()
p.once.Do(p.shutdown)
// wait for container to quit
p.cwg.Wait()
} }
func (p *Progress) shutdown() { // Shutdown cancels any running bar immediately and then shutdowns (*Progress)
close(p.done) // 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) { 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() error {
if s.bHeap.Len() == 0 {
render := func(debugOut io.Writer) { return nil
err := s.render(cw)
for err != nil {
if debugOut != nil {
_, err = fmt.Fprintln(debugOut, err)
} else {
panic(err)
}
debugOut = nil
} }
return s.render(cw)
} }
refreshCh := p.newTicker(s)
for { for {
select { select {
case op := <-p.operateState: case op := <-p.operateState:
op(s) op(s)
case <-p.refreshCh: case fn := <-p.interceptIo:
render(s.debugOut) fn(cw)
case <-s.shutdownNotifier: 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 { for s.heapUpdated {
render(s.debugOut) err := render()
if err != nil {
_, _ = fmt.Fprintln(s.debugOut, err.Error())
return
}
} }
return return
} }
@ -233,12 +311,13 @@ func (p *Progress) serve(s *pState, cw *cwriter.Writer) {
} }
func (s *pState) render(cw *cwriter.Writer) error { func (s *pState) render(cw *cwriter.Writer) error {
var wg sync.WaitGroup
if s.heapUpdated { if s.heapUpdated {
s.updateSyncMatrix() s.updateSyncMatrix()
s.heapUpdated = false s.heapUpdated = false
} }
syncWidth(s.pMatrix) syncWidth(&wg, s.pMatrix)
syncWidth(s.aMatrix) syncWidth(&wg, s.aMatrix)
width, height, err := cw.GetTermSize() width, height, err := cw.GetTermSize()
if err != nil { if err != nil {
@ -250,21 +329,25 @@ func (s *pState) render(cw *cwriter.Writer) error {
go bar.render(width) 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 { func (s *pState) flush(wg *sync.WaitGroup, cw *cwriter.Writer, height int) error {
var wg sync.WaitGroup
var popCount int var popCount int
rows := make([]io.Reader, 0, height)
pool := make([]*Bar, 0, s.bHeap.Len())
for s.bHeap.Len() > 0 { for s.bHeap.Len() > 0 {
var usedRows int
b := heap.Pop(&s.bHeap).(*Bar) b := heap.Pop(&s.bHeap).(*Bar)
frame := <-b.frameCh 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-- { for i := len(frame.rows) - 1; i >= 0; i-- {
if row := frame.rows[i]; len(rows) < height { if row := frame.rows[i]; len(s.rows) < height {
rows = append(rows, row) s.rows = append(s.rows, row)
usedRows++ usedRows++
} else { } else {
wg.Add(1) 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 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 { if qb, ok := s.queueBars[b]; ok {
delete(s.queueBars, b) delete(s.queueBars, b)
qb.priority = b.priority qb.priority = b.priority
pool = append(pool, qb) s.pool = append(s.pool, qb)
drop = true s.heapUpdated = true
} else if s.popCompleted && !b.bs.noPop { continue
if frame.shutdown > 1 {
popCount += usedRows
drop = true
} else {
s.popPriority++
b.priority = s.popPriority
}
} }
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 s.heapUpdated = true
continue continue
} }
} }
pool = append(pool, b) s.pool = append(s.pool, b)
} }
for _, b := range pool { switch len(s.pool) {
heap.Push(&s.bHeap, b) 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-- { for i := len(s.rows) - 1; i >= 0; i-- {
_, err := cw.ReadFrom(rows[i]) _, err := cw.ReadFrom(s.rows[i])
if err != nil { if err != nil {
wg.Wait()
return err return err
} }
} }
wg.Wait() err := cw.Flush(len(s.rows) - popCount)
return cw.Flush(len(rows) - popCount) s.rows = s.rows[:0]
} return err
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
} }
func (s *pState) updateSyncMatrix() { func (s *pState) updateSyncMatrix() {
@ -359,13 +424,12 @@ func (s *pState) updateSyncMatrix() {
for i := 0; i < s.bHeap.Len(); i++ { for i := 0; i < s.bHeap.Len(); i++ {
bar := s.bHeap[i] bar := s.bHeap[i]
table := bar.wSyncTable() 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) 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) 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 { func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOption) *bState {
bs := &bState{ bs := &bState{
id: s.idCount, id: s.idCount,
priority: s.idCount, priority: s.idCount,
reqWidth: s.reqWidth, reqWidth: s.reqWidth,
total: total, total: total,
filler: filler, filler: filler,
debugOut: s.debugOut, manualRefresh: s.manualRefresh,
} }
if total > 0 { if total > 0 {
@ -405,13 +469,14 @@ func (s *pState) makeBarState(total int64, filler BarFiller, options ...BarOptio
return bs return bs
} }
func syncWidth(matrix map[int][]chan int) { func syncWidth(wg *sync.WaitGroup, matrix map[int][]chan int) {
for _, column := range matrix { 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 var maxWidth int
for _, ch := range column { for _, ch := range column {
if w := <-ch; w > maxWidth { if w := <-ch; w > maxWidth {
@ -421,4 +486,5 @@ func maxWidthDistributor(column []chan int) {
for _, ch := range column { for _, ch := range column {
ch <- maxWidth 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 # github.com/containers/conmon v2.0.20+incompatible
## explicit ## explicit
github.com/containers/conmon/runner/config 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 ## explicit; go 1.17
github.com/containers/image/v5/copy github.com/containers/image/v5/copy
github.com/containers/image/v5/directory github.com/containers/image/v5/directory
@ -838,7 +838,7 @@ github.com/syndtr/gocapability/capability
# github.com/tchap/go-patricia v2.3.0+incompatible # github.com/tchap/go-patricia v2.3.0+incompatible
## explicit ## explicit
github.com/tchap/go-patricia/patricia 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 ## explicit; go 1.18
github.com/theupdateframework/go-tuf/encrypted github.com/theupdateframework/go-tuf/encrypted
# github.com/titanous/rocacheck v0.0.0-20171023193734-afe73141d399 # 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/archive/tar
github.com/vbatts/tar-split/tar/asm github.com/vbatts/tar-split/tar/asm
github.com/vbatts/tar-split/tar/storage github.com/vbatts/tar-split/tar/storage
# github.com/vbauerster/mpb/v7 v7.5.3 # github.com/vbauerster/mpb/v8 v8.1.4
## explicit; go 1.14 ## explicit; go 1.17
github.com/vbauerster/mpb/v7 github.com/vbauerster/mpb/v8
github.com/vbauerster/mpb/v7/cwriter github.com/vbauerster/mpb/v8/cwriter
github.com/vbauerster/mpb/v7/decor github.com/vbauerster/mpb/v8/decor
github.com/vbauerster/mpb/v7/internal github.com/vbauerster/mpb/v8/internal
# github.com/vishvananda/netlink v1.2.1-beta.2 # github.com/vishvananda/netlink v1.2.1-beta.2
## explicit; go 1.12 ## explicit; go 1.12
github.com/vishvananda/netlink github.com/vishvananda/netlink