๐Ÿ“ฆ tarampampam / tinifier

๐Ÿ“„ stats.go ยท 162 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162package cli

import (
	"fmt"
	"path/filepath"
	"strings"
	"sync"
	"unicode/utf8"

	"gh.tarampamp.am/tinifier/v5/internal/humanize"
)

type fileStat struct {
	Path, Type         string
	OrigSize, CompSize uint64
	Skipped            bool
}

type fileStats struct {
	Items []fileStat
	mu    sync.Mutex
}

func (fs *fileStats) Add(v fileStat) {
	fs.mu.Lock()
	fs.Items = append(fs.Items, v)
	fs.mu.Unlock()
}

func (fs *fileStats) Table() string { //nolint:funlen
	if len(fs.Items) == 0 {
		return ""
	}

	var (
		columns = make(map[string][4]string, len(fs.Items))

		longestFileName int
		longestType     int
		longestDiffSize int

		totalOrig    int
		totalComp    int
		totalSkipped int
	)

	for _, item := range fs.Items {
		var (
			fileName = filepath.Base(item.Path)
			typeName = item.Type
			diffSize = fmt.Sprintf("%s โ†’ %s",
				humanize.Bytes(item.OrigSize),
				humanize.Bytes(item.CompSize),
			)
			deltaSize = fmt.Sprintf("%s, %s",
				humanize.BytesDiff(item.CompSize, item.OrigSize),
				humanize.PercentageDiff(item.CompSize, item.OrigSize),
			)
		)

		if v := utf8.RuneCountInString(fileName); v > longestFileName {
			longestFileName = v
		}

		if v := utf8.RuneCountInString(typeName); v > longestType {
			longestType = v
		}

		if v := utf8.RuneCountInString(diffSize); v > longestDiffSize {
			longestDiffSize = v
		}

		totalOrig += int(item.OrigSize) //nolint:gosec
		totalComp += int(item.CompSize) //nolint:gosec

		if item.Skipped {
			totalSkipped++
		}

		columns[item.Path] = [4]string{fileName, typeName, diffSize, deltaSize}
	}

	var b strings.Builder

	b.Grow(len(fs.Items) * (longestFileName + longestType + longestDiffSize + 32)) //nolint:mnd // preallocate buffer

	const pad = "  "

	for i, item := range fs.Items {
		b.WriteRune(' ')

		if item.Skipped {
			b.WriteRune('โœ˜')
		} else {
			b.WriteRune('โœ”')
		}

		b.WriteRune(' ')

		row := columns[item.Path]
		fileName, typeName, diffSize, deltaSize := row[0], row[1], row[2], row[3]

		b.WriteString(fileName)
		b.WriteString(strings.Repeat(" ", max(0, longestFileName-utf8.RuneCountInString(fileName))))
		b.WriteString(pad)

		b.WriteString(typeName)
		b.WriteString(strings.Repeat(" ", max(0, longestType-utf8.RuneCountInString(typeName))))
		b.WriteString(pad)

		if !item.Skipped {
			b.WriteString(diffSize)
			b.WriteString(strings.Repeat(" ", max(0, longestDiffSize-utf8.RuneCountInString(diffSize))))
			b.WriteString(pad)

			b.WriteRune('(')
			b.WriteString(deltaSize)
			b.WriteRune(')')
		} else {
			b.WriteString("(skipped)")
		}

		if i < len(fs.Items)-1 {
			b.WriteRune('\n')
		}
	}

	if l := len(fs.Items); l > 1 && totalSkipped < l {
		b.WriteRune('\n')
		b.WriteString("   ") // [space][emoji][space]
		b.WriteString(strings.Repeat(" ", max(0, longestFileName)))
		b.WriteString(pad)

		const total = "Total:"

		b.WriteString(total)
		b.WriteString(strings.Repeat(" ", max(0, longestType-utf8.RuneCountInString(total))))
		b.WriteString(pad)

		var (
			diffSize = fmt.Sprintf("%s โ†’ %s",
				humanize.Bytes(totalOrig),
				humanize.Bytes(totalComp),
			)
			deltaSize = fmt.Sprintf("%s, %s",
				humanize.BytesDiff(totalOrig, totalComp),
				humanize.PercentageDiff(totalOrig, totalComp),
			)
		)

		b.WriteString(diffSize)
		b.WriteString(strings.Repeat(" ", max(0, longestDiffSize-utf8.RuneCountInString(diffSize))))
		b.WriteString(pad)

		b.WriteRune('(')
		b.WriteString(deltaSize)
		b.WriteRune(')')
	}

	return b.String()
}