diff --git a/internal/util/bytesize.go b/internal/util/bytesize.go index 9403af8..091d3c5 100644 --- a/internal/util/bytesize.go +++ b/internal/util/bytesize.go @@ -4,15 +4,21 @@ import ( "fmt" "math" "strconv" + "strings" "github.com/dustin/go-humanize" ) // ByteSize is a size in bytes, configured as a string such as "32KiB", "1MB" // or "1.5 GB" (see ParseByteSize). KB, MB, ... are powers of 1000 and KiB, -// MiB, ... powers of 1024; a bare number is bytes. +// MiB, ... powers of 1024; a bare number is bytes. ByteSizeNoLimit (-1) means +// no limit; no other size is negative. type ByteSize int64 +// ByteSizeNoLimit is the size that means no limit, written "-1". A setting +// for which no limit is unsafe rejects it. +const ByteSizeNoLimit ByteSize = -1 + // byteUnits are the units String may use, largest first. var byteUnits = []struct { name string @@ -27,8 +33,16 @@ var byteUnits = []struct { } // ParseByteSize parses a size such as "32KiB", "1 MB" or "4096". Unit names -// are case-insensitive. +// are case-insensitive. "-1" (or "-1B") is ByteSizeNoLimit; any other +// negative size is an error. func ParseByteSize(s string) (ByteSize, error) { + trimmed := strings.TrimSpace(s) + if trimmed == "-1" || strings.EqualFold(trimmed, "-1B") { + return ByteSizeNoLimit, nil + } + if strings.HasPrefix(trimmed, "-") { + return 0, fmt.Errorf("byte size %q is negative; only -1 (no limit) is allowed", s) + } n, err := humanize.ParseBytes(s) if err != nil { return 0, err @@ -41,7 +55,11 @@ func ParseByteSize(s string) (ByteSize, error) { // String formats b exactly, in the largest unit that divides it (e.g. "32KiB", // "1MB"), or in bytes ("1500B"), so it parses back to the same value. +// ByteSizeNoLimit is "-1". func (b ByteSize) String() string { + if b == ByteSizeNoLimit { + return "-1" + } if b != 0 { for _, u := range byteUnits { if int64(b)%u.size == 0 { diff --git a/internal/util/bytesize_test.go b/internal/util/bytesize_test.go index 3b118c6..0861f7e 100644 --- a/internal/util/bytesize_test.go +++ b/internal/util/bytesize_test.go @@ -1,6 +1,9 @@ package util -import "testing" +import ( + "strings" + "testing" +) func TestByteSizeString(t *testing.T) { tests := []struct { @@ -25,3 +28,23 @@ func TestByteSizeString(t *testing.T) { } } } + +// -1 is ByteSizeNoLimit, parsed and formatted as "-1"; no other size may be +// negative. +func TestByteSizeNoLimit(t *testing.T) { + for _, text := range []string{"-1", "-1B", "-1b", " -1 "} { + size, err := ParseByteSize(text) + if err != nil || size != ByteSizeNoLimit { + t.Errorf("ParseByteSize(%q) = %d, %v; want ByteSizeNoLimit", text, size, err) + } + } + if got := ByteSizeNoLimit.String(); got != "-1" { + t.Errorf("ByteSizeNoLimit.String() = %q; want -1", got) + } + for _, text := range []string{"-2", "-1KB", "-0.5", "-1KiB", "-100"} { + size, err := ParseByteSize(text) + if err == nil || !strings.Contains(err.Error(), "only -1 (no limit)") { + t.Errorf("ParseByteSize(%q) = %d, %v; want a negative size error", text, size, err) + } + } +}