mirror of https://github.com/containers/podman.git
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:
parent
e64508378f
commit
be47eeb85c
6
go.mod
6
go.mod
|
@ -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
14
go.sum
|
@ -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=
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -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"
|
||||||
)
|
)
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
|
|
|
@ -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"`
|
||||||
}
|
}
|
||||||
|
|
|
@ -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.
|
||||||
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
|
|
@ -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)
|
|
||||||
}
|
|
|
@ -1,8 +1,8 @@
|
||||||
# Multi Progress Bar
|
# Multi Progress Bar
|
||||||
|
|
||||||
[](https://pkg.go.dev/github.com/vbauerster/mpb/v7)
|
[](https://pkg.go.dev/github.com/vbauerster/mpb/v8)
|
||||||
[](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
|
[](https://github.com/vbauerster/mpb/actions/workflows/test.yml)
|
||||||
[](https://www.paypal.me/vbauerster)
|
[](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))
|
|
||||||
}
|
}
|
||||||
}()
|
}()
|
||||||
}
|
}
|
429
vendor/github.com/vbauerster/mpb/v7/bar.go → vendor/github.com/vbauerster/mpb/v8/bar.go
generated
vendored
429
vendor/github.com/vbauerster/mpb/v7/bar.go → vendor/github.com/vbauerster/mpb/v8/bar.go
generated
vendored
|
@ -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)
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -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()
|
|
||||||
}
|
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
})
|
||||||
})
|
})
|
||||||
}
|
}
|
|
@ -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
|
||||||
}
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -1,4 +1,4 @@
|
||||||
// +build darwin dragonfly freebsd netbsd openbsd
|
//go:build darwin || dragonfly || freebsd || netbsd || openbsd
|
||||||
|
|
||||||
package cwriter
|
package cwriter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build aix linux
|
//go:build aix || linux
|
||||||
|
|
||||||
package cwriter
|
package cwriter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build solaris
|
//go:build solaris
|
||||||
|
|
||||||
package cwriter
|
package cwriter
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
// +build zos
|
//go:build zos
|
||||||
|
|
||||||
package cwriter
|
package cwriter
|
||||||
|
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
|
@ -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}
|
||||||
}
|
}
|
|
@ -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 == "" {
|
|
@ -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.
|
|
@ -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:
|
||||||
//
|
//
|
|
@ -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)
|
|
@ -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...),
|
|
@ -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
|
|
@ -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...)
|
||||||
}
|
}
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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"
|
|
@ -0,0 +1,10 @@
|
||||||
|
package decor
|
||||||
|
|
||||||
|
import "sync"
|
||||||
|
|
||||||
|
var bytePool = sync.Pool{
|
||||||
|
New: func() interface{} {
|
||||||
|
b := make([]byte, 0, 16)
|
||||||
|
return &b
|
||||||
|
},
|
||||||
|
}
|
|
@ -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)
|
||||||
}
|
}
|
|
@ -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"
|
|
@ -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()
|
||||||
}
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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)
|
||||||
|
}
|
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue