New types CounterKiB and CounterKB
Vladimir Bauer
8 years ago
| 0 | package decor | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "strconv" | |
| 5 | "strings" | |
| 6 | ) | |
| 7 | ||
| 8 | const ( | |
| 9 | _ = iota | |
| 10 | KiB = 1 << (iota * 10) | |
| 11 | MiB | |
| 12 | GiB | |
| 13 | TiB | |
| 14 | ) | |
| 15 | ||
| 16 | const ( | |
| 17 | KB = 1000 | |
| 18 | MB = KB * 1000 | |
| 19 | GB = MB * 1000 | |
| 20 | TB = GB * 1000 | |
| 21 | ) | |
| 22 | ||
| 23 | const ( | |
| 24 | _ = iota | |
| 25 | // Unit_KiB Kibibyte = 1024 b | |
| 26 | Unit_KiB | |
| 27 | // Unit_kB Kilobyte = 1000 b | |
| 28 | Unit_kB | |
| 29 | ) | |
| 30 | ||
| 31 | type Unit uint | |
| 32 | ||
| 33 | type CounterKiB int64 | |
| 34 | ||
| 35 | func (c CounterKiB) Format(f fmt.State, r rune) { | |
| 36 | prec, ok := f.Precision() | |
| 37 | ||
| 38 | if r == 'd' || !ok { | |
| 39 | prec = 0 | |
| 40 | } | |
| 41 | if r == 'f' && !ok { | |
| 42 | prec = 6 | |
| 43 | } | |
| 44 | // retain old beahavior if s verb used | |
| 45 | if r == 's' { | |
| 46 | prec = 1 | |
| 47 | } | |
| 48 | ||
| 49 | var res, unit string | |
| 50 | switch { | |
| 51 | case c >= TiB: | |
| 52 | unit = "TiB" | |
| 53 | res = strconv.FormatFloat(float64(c)/TiB, 'f', prec, 64) | |
| 54 | case c >= GiB: | |
| 55 | unit = "GiB" | |
| 56 | res = strconv.FormatFloat(float64(c)/GiB, 'f', prec, 64) | |
| 57 | case c >= MiB: | |
| 58 | unit = "MiB" | |
| 59 | res = strconv.FormatFloat(float64(c)/MiB, 'f', prec, 64) | |
| 60 | case c >= KiB: | |
| 61 | unit = "KiB" | |
| 62 | res = strconv.FormatFloat(float64(c)/KiB, 'f', prec, 64) | |
| 63 | default: | |
| 64 | unit = "b" | |
| 65 | res = strconv.FormatInt(int64(c), 10) | |
| 66 | } | |
| 67 | ||
| 68 | if f.Flag(int(' ')) { | |
| 69 | res += " " | |
| 70 | } | |
| 71 | res += unit | |
| 72 | ||
| 73 | if w, ok := f.Width(); ok { | |
| 74 | if len(res) < w { | |
| 75 | pad := strings.Repeat(" ", w-len(res)) | |
| 76 | if f.Flag(int('-')) { | |
| 77 | res += pad | |
| 78 | } else { | |
| 79 | res = pad + res | |
| 80 | } | |
| 81 | } | |
| 82 | } | |
| 83 | ||
| 84 | f.Write([]byte(res)) | |
| 85 | } | |
| 86 | ||
| 87 | type CounterKB int64 | |
| 88 | ||
| 89 | func (c CounterKB) Format(f fmt.State, r rune) { | |
| 90 | prec, ok := f.Precision() | |
| 91 | ||
| 92 | if r == 'd' || !ok { | |
| 93 | prec = 0 | |
| 94 | } | |
| 95 | if r == 'f' && !ok { | |
| 96 | prec = 6 | |
| 97 | } | |
| 98 | // retain old beahavior if s verb used | |
| 99 | if r == 's' { | |
| 100 | prec = 1 | |
| 101 | } | |
| 102 | ||
| 103 | var res, unit string | |
| 104 | switch { | |
| 105 | case c >= TB: | |
| 106 | unit = "TB" | |
| 107 | res = strconv.FormatFloat(float64(c)/TB, 'f', prec, 64) | |
| 108 | case c >= GB: | |
| 109 | unit = "GB" | |
| 110 | res = strconv.FormatFloat(float64(c)/GB, 'f', prec, 64) | |
| 111 | case c >= MB: | |
| 112 | unit = "MB" | |
| 113 | res = strconv.FormatFloat(float64(c)/MB, 'f', prec, 64) | |
| 114 | case c >= KB: | |
| 115 | unit = "kB" | |
| 116 | res = strconv.FormatFloat(float64(c)/KB, 'f', prec, 64) | |
| 117 | default: | |
| 118 | unit = "b" | |
| 119 | res = strconv.FormatInt(int64(c), 10) | |
| 120 | } | |
| 121 | ||
| 122 | if f.Flag(int(' ')) { | |
| 123 | res += " " | |
| 124 | } | |
| 125 | res += unit | |
| 126 | ||
| 127 | if w, ok := f.Width(); ok { | |
| 128 | if len(res) < w { | |
| 129 | pad := strings.Repeat(" ", w-len(res)) | |
| 130 | if f.Flag(int('-')) { | |
| 131 | res += pad | |
| 132 | } else { | |
| 133 | res = pad + res | |
| 134 | } | |
| 135 | } | |
| 136 | } | |
| 137 | ||
| 138 | f.Write([]byte(res)) | |
| 139 | } |
| 0 | package decor | |
| 1 | ||
| 2 | import ( | |
| 3 | "fmt" | |
| 4 | "testing" | |
| 5 | ) | |
| 6 | ||
| 7 | func TestCounterKiB(t *testing.T) { | |
| 8 | cases := map[string]struct { | |
| 9 | value int64 | |
| 10 | verb, expected string | |
| 11 | }{ | |
| 12 | "verb %f": {12345678, "%f", "11.773756MiB"}, | |
| 13 | "verb %.0f": {12345678, "%.0f", "12MiB"}, | |
| 14 | "verb %.1f": {12345678, "%.1f", "11.8MiB"}, | |
| 15 | "verb %.2f": {12345678, "%.2f", "11.77MiB"}, | |
| 16 | "verb %.3f": {12345678, "%.3f", "11.774MiB"}, | |
| 17 | ||
| 18 | "verb % f": {12345678, "% f", "11.773756 MiB"}, | |
| 19 | "verb % .0f": {12345678, "% .0f", "12 MiB"}, | |
| 20 | "verb % .1f": {12345678, "% .1f", "11.8 MiB"}, | |
| 21 | "verb % .2f": {12345678, "% .2f", "11.77 MiB"}, | |
| 22 | "verb % .3f": {12345678, "% .3f", "11.774 MiB"}, | |
| 23 | ||
| 24 | "verb %8.f": {12345678, "%8.f", " 12MiB"}, | |
| 25 | "verb %8.0f": {12345678, "%8.0f", " 12MiB"}, | |
| 26 | "verb %8.1f": {12345678, "%8.1f", " 11.8MiB"}, | |
| 27 | "verb %8.2f": {12345678, "%8.2f", "11.77MiB"}, | |
| 28 | "verb %8.3f": {12345678, "%8.3f", "11.774MiB"}, | |
| 29 | ||
| 30 | "verb % 8.f": {12345678, "% 8.f", " 12 MiB"}, | |
| 31 | "verb % 8.0f": {12345678, "% 8.0f", " 12 MiB"}, | |
| 32 | "verb % 8.1f": {12345678, "% 8.1f", "11.8 MiB"}, | |
| 33 | ||
| 34 | "verb %-8.f": {12345678, "%-8.f", "12MiB "}, | |
| 35 | "verb %-8.0f": {12345678, "%-8.0f", "12MiB "}, | |
| 36 | "verb %-8.1f": {12345678, "%-8.1f", "11.8MiB "}, | |
| 37 | "verb %-8.2f": {12345678, "%8.2f", "11.77MiB"}, | |
| 38 | "verb %-8.3f": {12345678, "%8.3f", "11.774MiB"}, | |
| 39 | ||
| 40 | "verb % -8.f": {12345678, "% -8.f", "12 MiB "}, | |
| 41 | "verb % -8.0f": {12345678, "% -8.0f", "12 MiB "}, | |
| 42 | "verb % -8.1f": {12345678, "% -8.1f", "11.8 MiB"}, | |
| 43 | ||
| 44 | "1000b %f": {1000, "%f", "1000b"}, | |
| 45 | "1000b %d": {1000, "%d", "1000b"}, | |
| 46 | "1000b %s": {1000, "%d", "1000b"}, | |
| 47 | "1024 %f": {1024, "%f", "1.000000KiB"}, | |
| 48 | "1024 %d": {1024, "%d", "1KiB"}, | |
| 49 | "1024 %.1f": {1024, "%.1f", "1.0KiB"}, | |
| 50 | "1024 %s": {1024, "%s", "1.0KiB"}, | |
| 51 | "3*MiB+140KiB %f": {3*MiB + 140*KiB, "%f", "3.136719MiB"}, | |
| 52 | "3*MiB+140KiB %d": {3*MiB + 140*KiB, "%d", "3MiB"}, | |
| 53 | "3*MiB+140KiB %.1f": {3*MiB + 140*KiB, "%.1f", "3.1MiB"}, | |
| 54 | "3*MiB+140KiB %s": {3*MiB + 140*KiB, "%s", "3.1MiB"}, | |
| 55 | "2*GiB %f": {2 * GiB, "%f", "2.000000GiB"}, | |
| 56 | "2*GiB %d": {2 * GiB, "%d", "2GiB"}, | |
| 57 | "2*GiB %.1f": {2 * GiB, "%.1f", "2.0GiB"}, | |
| 58 | "2*GiB %s": {2 * GiB, "%s", "2.0GiB"}, | |
| 59 | "4*TiB %f": {4 * TiB, "%f", "4.000000TiB"}, | |
| 60 | "4*TiB %d": {4 * TiB, "%d", "4TiB"}, | |
| 61 | "4*TiB %.1f": {4 * TiB, "%.1f", "4.0TiB"}, | |
| 62 | "4*TiB %s": {4 * TiB, "%s", "4.0TiB"}, | |
| 63 | } | |
| 64 | for k, tc := range cases { | |
| 65 | got := fmt.Sprintf(tc.verb, CounterKiB(tc.value)) | |
| 66 | if got != tc.expected { | |
| 67 | t.Errorf("%s: Expected: %q, got: %q\n", k, tc.expected, got) | |
| 68 | } | |
| 69 | } | |
| 70 | } | |
| 71 | ||
| 72 | func TestCounterKB(t *testing.T) { | |
| 73 | cases := map[string]struct { | |
| 74 | value int64 | |
| 75 | verb, expected string | |
| 76 | }{ | |
| 77 | "verb %f": {12345678, "%f", "12.345678MB"}, | |
| 78 | "verb %.0f": {12345678, "%.0f", "12MB"}, | |
| 79 | "verb %.1f": {12345678, "%.1f", "12.3MB"}, | |
| 80 | "verb %.2f": {12345678, "%.2f", "12.35MB"}, | |
| 81 | "verb %.3f": {12345678, "%.3f", "12.346MB"}, | |
| 82 | ||
| 83 | "verb % f": {12345678, "% f", "12.345678 MB"}, | |
| 84 | "verb % .0f": {12345678, "% .0f", "12 MB"}, | |
| 85 | "verb % .1f": {12345678, "% .1f", "12.3 MB"}, | |
| 86 | "verb % .2f": {12345678, "% .2f", "12.35 MB"}, | |
| 87 | "verb % .3f": {12345678, "% .3f", "12.346 MB"}, | |
| 88 | ||
| 89 | "verb %8.f": {12345678, "%8.f", " 12MB"}, | |
| 90 | "verb %8.0f": {12345678, "%8.0f", " 12MB"}, | |
| 91 | "verb %8.1f": {12345678, "%8.1f", " 12.3MB"}, | |
| 92 | "verb %8.2f": {12345678, "%8.2f", " 12.35MB"}, | |
| 93 | "verb %8.3f": {12345678, "%8.3f", "12.346MB"}, | |
| 94 | ||
| 95 | "verb % 8.f": {12345678, "% 8.f", " 12 MB"}, | |
| 96 | "verb % 8.0f": {12345678, "% 8.0f", " 12 MB"}, | |
| 97 | "verb % 8.1f": {12345678, "% 8.1f", " 12.3 MB"}, | |
| 98 | ||
| 99 | "verb %-8.f": {12345678, "%-8.f", "12MB "}, | |
| 100 | "verb %-8.0f": {12345678, "%-8.0f", "12MB "}, | |
| 101 | "verb %-8.1f": {12345678, "%-8.1f", "12.3MB "}, | |
| 102 | "verb %-8.2f": {12345678, "%8.2f", " 12.35MB"}, | |
| 103 | "verb %-8.3f": {12345678, "%8.3f", "12.346MB"}, | |
| 104 | ||
| 105 | "verb % -8.f": {12345678, "% -8.f", "12 MB "}, | |
| 106 | "verb % -8.0f": {12345678, "% -8.0f", "12 MB "}, | |
| 107 | "verb % -8.1f": {12345678, "% -8.1f", "12.3 MB "}, | |
| 108 | ||
| 109 | "1000b %f": {1000, "%f", "1.000000kB"}, | |
| 110 | "1000b %d": {1000, "%d", "1kB"}, | |
| 111 | "1000b %s": {1000, "%d", "1kB"}, | |
| 112 | "1024 %f": {1024, "%f", "1.024000kB"}, | |
| 113 | "1024 %d": {1024, "%d", "1kB"}, | |
| 114 | "1024 %.1f": {1024, "%.1f", "1.0kB"}, | |
| 115 | "1024 %s": {1024, "%s", "1.0kB"}, | |
| 116 | "3*MB+140*KB %f": {3*MB + 140*KB, "%f", "3.140000MB"}, | |
| 117 | "3*MB+140*KB %d": {3*MB + 140*KB, "%d", "3MB"}, | |
| 118 | "3*MB+140*KB %.1f": {3*MB + 140*KB, "%.1f", "3.1MB"}, | |
| 119 | "3*MB+140*KB %s": {3*MB + 140*KB, "%s", "3.1MB"}, | |
| 120 | "2*GB %f": {2 * GB, "%f", "2.000000GB"}, | |
| 121 | "2*GB %d": {2 * GB, "%d", "2GB"}, | |
| 122 | "2*GB %.1f": {2 * GB, "%.1f", "2.0GB"}, | |
| 123 | "2*GB %s": {2 * GB, "%s", "2.0GB"}, | |
| 124 | "4*TB %f": {4 * TB, "%f", "4.000000TB"}, | |
| 125 | "4*TB %d": {4 * TB, "%d", "4TB"}, | |
| 126 | "4*TB %.1f": {4 * TB, "%.1f", "4.0TB"}, | |
| 127 | "4*TB %s": {4 * TB, "%s", "4.0TB"}, | |
| 128 | } | |
| 129 | for k, tc := range cases { | |
| 130 | got := fmt.Sprintf(tc.verb, CounterKB(tc.value)) | |
| 131 | if got != tc.expected { | |
| 132 | t.Errorf("%s: Expected: %q, got: %q\n", k, tc.expected, got) | |
| 133 | } | |
| 134 | } | |
| 135 | } |
| 85 | 85 | } |
| 86 | 86 | |
| 87 | 87 | // Counters provides basic counters decorator. |
| 88 | // Accepts pairFormat string, something like "%s / %s" to be used in | |
| 89 | // fmt.Sprintf(pairFormat, current, total) and one of (Unit_KiB/Unit_kB) | |
| 90 | // constant. If there're more than one bar, and you'd like to synchronize column | |
| 91 | // width, conf param should have DwidthSync bit set. | |
| 92 | func Counters(pairFormat string, unit Units, minWidth int, conf byte) DecoratorFunc { | |
| 93 | format := "%%" | |
| 94 | if (conf & DidentRight) != 0 { | |
| 95 | format += "-" | |
| 96 | } | |
| 97 | format += "%ds" | |
| 98 | return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { | |
| 99 | current := Format(s.Current).To(unit) | |
| 100 | total := Format(s.Total).To(unit) | |
| 101 | str := fmt.Sprintf(pairFormat, current, total) | |
| 88 | // pairFormat must contain two printf compatible verbs, like "%f" or "%d". | |
| 89 | // First verb substituted with Current, second one with Total. For example (assuming decor.Unit_KiB used): | |
| 90 | // "%.1f / %.1f" = "1.0MiB / 12.0MiB" or "% .1f / % .1f" = "1.0 MiB / 12.0 MiB" | |
| 91 | // unit is one of decor.Unit_KiB/decor.Unit_kB or just zero if you need raw unitless numbers. | |
| 92 | func Counters(pairFormat string, unit Unit, minWidth int, conf byte) DecoratorFunc { | |
| 93 | format := "%%" | |
| 94 | if (conf & DidentRight) != 0 { | |
| 95 | format += "-" | |
| 96 | } | |
| 97 | format += "%ds" | |
| 98 | return func(s *Statistics, myWidth chan<- int, maxWidth <-chan int) string { | |
| 99 | var str string | |
| 100 | switch unit { | |
| 101 | case Unit_KiB: | |
| 102 | str = fmt.Sprintf(pairFormat, CounterKiB(s.Current), CounterKiB(s.Total)) | |
| 103 | case Unit_kB: | |
| 104 | str = fmt.Sprintf(pairFormat, CounterKB(s.Current), CounterKB(s.Total)) | |
| 105 | default: | |
| 106 | str = fmt.Sprintf(pairFormat, s.Current, s.Total) | |
| 107 | } | |
| 102 | 108 | if (conf & DwidthSync) != 0 { |
| 103 | 109 | myWidth <- utf8.RuneCountInString(str) |
| 104 | 110 | max := <-maxWidth |
| 0 | package decor | |
| 1 | ||
| 2 | import "fmt" | |
| 3 | ||
| 4 | const ( | |
| 5 | _ = iota | |
| 6 | KiB = 1 << (iota * 10) | |
| 7 | MiB | |
| 8 | GiB | |
| 9 | TiB | |
| 10 | ) | |
| 11 | ||
| 12 | const ( | |
| 13 | KB = 1000 | |
| 14 | MB = KB * 1000 | |
| 15 | GB = MB * 1000 | |
| 16 | TB = GB * 1000 | |
| 17 | ) | |
| 18 | ||
| 19 | const ( | |
| 20 | _ = iota | |
| 21 | // Unit_KiB Kibibyte = 1024 b | |
| 22 | Unit_KiB | |
| 23 | // Unit_kB Kilobyte = 1000 b | |
| 24 | Unit_kB | |
| 25 | ) | |
| 26 | ||
| 27 | type Units uint | |
| 28 | ||
| 29 | func Format(i int64) *formatter { | |
| 30 | return &formatter{n: i} | |
| 31 | } | |
| 32 | ||
| 33 | type formatter struct { | |
| 34 | n int64 | |
| 35 | unit Units | |
| 36 | width int | |
| 37 | } | |
| 38 | ||
| 39 | func (f *formatter) To(unit Units) *formatter { | |
| 40 | f.unit = unit | |
| 41 | return f | |
| 42 | } | |
| 43 | ||
| 44 | func (f *formatter) Width(width int) *formatter { | |
| 45 | f.width = width | |
| 46 | return f | |
| 47 | } | |
| 48 | ||
| 49 | func (f *formatter) String() string { | |
| 50 | switch f.unit { | |
| 51 | case Unit_KiB: | |
| 52 | return formatKiB(f.n) | |
| 53 | case Unit_kB: | |
| 54 | return formatKB(f.n) | |
| 55 | default: | |
| 56 | return fmt.Sprintf(fmt.Sprintf("%%%dd", f.width), f.n) | |
| 57 | } | |
| 58 | } | |
| 59 | ||
| 60 | func formatKiB(i int64) (result string) { | |
| 61 | switch { | |
| 62 | case i >= TiB: | |
| 63 | result = fmt.Sprintf("%.1fTiB", float64(i)/TiB) | |
| 64 | case i >= GiB: | |
| 65 | result = fmt.Sprintf("%.1fGiB", float64(i)/GiB) | |
| 66 | case i >= MiB: | |
| 67 | result = fmt.Sprintf("%.1fMiB", float64(i)/MiB) | |
| 68 | case i >= KiB: | |
| 69 | result = fmt.Sprintf("%.1fKiB", float64(i)/KiB) | |
| 70 | default: | |
| 71 | result = fmt.Sprintf("%db", i) | |
| 72 | } | |
| 73 | return | |
| 74 | } | |
| 75 | ||
| 76 | func formatKB(i int64) (result string) { | |
| 77 | switch { | |
| 78 | case i >= TB: | |
| 79 | result = fmt.Sprintf("%.1fTB", float64(i)/TB) | |
| 80 | case i >= GB: | |
| 81 | result = fmt.Sprintf("%.1fGB", float64(i)/GB) | |
| 82 | case i >= MB: | |
| 83 | result = fmt.Sprintf("%.1fMB", float64(i)/MB) | |
| 84 | case i >= KB: | |
| 85 | result = fmt.Sprintf("%.1fkB", float64(i)/KB) | |
| 86 | default: | |
| 87 | result = fmt.Sprintf("%db", i) | |
| 88 | } | |
| 89 | return | |
| 90 | } |
| 0 | package decor_test | |
| 1 | ||
| 2 | import ( | |
| 3 | "testing" | |
| 4 | ||
| 5 | "github.com/vbauerster/mpb/decor" | |
| 6 | ) | |
| 7 | ||
| 8 | func TestFormatNoUnits(t *testing.T) { | |
| 9 | actual := decor.Format(1234567).String() | |
| 10 | expected := "1234567" | |
| 11 | if actual != expected { | |
| 12 | t.Errorf("Expected %q but found %q", expected, actual) | |
| 13 | } | |
| 14 | } | |
| 15 | ||
| 16 | func TestFormatWidth(t *testing.T) { | |
| 17 | actual := decor.Format(1234567).Width(10).String() | |
| 18 | expected := " 1234567" | |
| 19 | if actual != expected { | |
| 20 | t.Errorf("Expected %q but found %q", expected, actual) | |
| 21 | } | |
| 22 | } | |
| 23 | ||
| 24 | func TestFormatToBytes(t *testing.T) { | |
| 25 | inputs := []struct { | |
| 26 | v int64 | |
| 27 | e string | |
| 28 | }{ | |
| 29 | {v: 1000, e: "1000b"}, | |
| 30 | {v: 1024, e: "1.0KiB"}, | |
| 31 | {v: 3*decor.MiB + 140*decor.KiB, e: "3.1MiB"}, | |
| 32 | {v: 2 * decor.GiB, e: "2.0GiB"}, | |
| 33 | {v: 4 * decor.TiB, e: "4.0TiB"}, | |
| 34 | } | |
| 35 | ||
| 36 | for _, input := range inputs { | |
| 37 | actual := decor.Format(input.v).To(decor.Unit_KiB).String() | |
| 38 | if actual != input.e { | |
| 39 | t.Errorf("Expected %q but found %q", input.e, actual) | |
| 40 | } | |
| 41 | } | |
| 42 | } |