Skip to content

Commit eff50e3

Browse files
authored
feat: support human-readable data storage size units in config (#103)
1 parent 7ef235f commit eff50e3

File tree

6 files changed

+127
-6
lines changed

6 files changed

+127
-6
lines changed

example.jlv.jsonc

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,5 +65,15 @@
6565
},
6666
// The maximum size of the file in bytes.
6767
// The rest of the file will be ignored.
68-
"maxFileSizeBytes": 1073741824,
68+
//
69+
// It accepts the number of bytes:
70+
//
71+
// "maxFileSizeBytes": 1073741824
72+
//
73+
// Or a text value with a unit:
74+
//
75+
// "maxFileSizeBytes": "1000k"
76+
// "maxFileSizeBytes": "1.5m"
77+
// "maxFileSizeBytes": "1g"
78+
"maxFileSizeBytes": "2g"
6979
}

go.mod

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ require (
1111
github.com/charmbracelet/bubbles v0.20.0
1212
github.com/charmbracelet/bubbletea v1.1.1
1313
github.com/charmbracelet/lipgloss v0.13.0
14+
github.com/docker/go-units v0.5.0
1415
github.com/go-playground/validator/v10 v10.22.1
1516
github.com/hedhyw/jsoncjson v1.1.0
1617
github.com/hedhyw/semerr v0.6.7

go.sum

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ github.com/charmbracelet/x/term v0.2.0/go.mod h1:GVxgxAbjUrmpvIINHIQnJJKpMlHiZ4c
1717
github.com/creack/pty v1.1.9/go.mod h1:oKZEueFk5CKHvIhNR5MUki03XCEU+Q6VDXinZuGJ33E=
1818
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
1919
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
20+
github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4=
21+
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
2022
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f h1:Y/CXytFA4m6baUTXGLOoWe4PQhGxaX0KpnayAqC48p4=
2123
github.com/erikgeiser/coninput v0.0.0-20211004153227-1c3628e74d0f/go.mod h1:vw97MGsxSvLiUE2X8qFplwetxpGLQrlU1Q9AUEIzCaM=
2224
github.com/gabriel-vasile/mimetype v1.4.5 h1:J7wGKdGu33ocBOhGy0z653k/lFKLFDPJMG8Gql0kxn4=

internal/pkg/config/config.go

Lines changed: 35 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,9 @@ import (
55
"errors"
66
"fmt"
77
"os"
8+
"strconv"
89

10+
units "github.com/docker/go-units"
911
"github.com/go-playground/validator/v10"
1012
"github.com/hedhyw/jsoncjson"
1113
)
@@ -23,7 +25,7 @@ type Config struct {
2325
CustomLevelMapping map[string]string `json:"customLevelMapping"`
2426

2527
// MaxFileSizeBytes is the maximum size of the file to load.
26-
MaxFileSizeBytes int64 `json:"maxFileSizeBytes" validate:"min=1"`
28+
MaxFileSizeBytes ByteSize `json:"maxFileSizeBytes" validate:"min=1"`
2729
}
2830

2931
// FieldKind describes the type of the log field.
@@ -55,7 +57,7 @@ func GetDefaultConfig() *Config {
5557
return &Config{
5658
Path: "default",
5759
CustomLevelMapping: GetDefaultCustomLevelMapping(),
58-
MaxFileSizeBytes: 1024 * 1024 * 1024,
60+
MaxFileSizeBytes: 2 * units.GB,
5961
Fields: []Field{{
6062
Title: "Time",
6163
Kind: FieldKindNumericTime,
@@ -149,3 +151,34 @@ func GetDefaultCustomLevelMapping() map[string]string {
149151
"60": "fatal",
150152
}
151153
}
154+
155+
// ByteSize supports decoding from byte count or number with unit.
156+
//
157+
// Example: 1k, 1.5m, 1g, 1t, 1p.
158+
type ByteSize int
159+
160+
// UnmarshalJSON implements json.Unmarshaler.
161+
func (s *ByteSize) UnmarshalJSON(text []byte) error {
162+
value := string(text)
163+
164+
valueUnquoted, err := strconv.Unquote(value)
165+
if err == nil {
166+
value = valueUnquoted
167+
}
168+
169+
parsedBytes, err := strconv.Atoi(value)
170+
if err == nil {
171+
*s = ByteSize(parsedBytes)
172+
173+
return nil
174+
}
175+
176+
size, err := units.FromHumanSize(value)
177+
if err != nil {
178+
return fmt.Errorf("parsing unit: %w", err)
179+
}
180+
181+
*s = ByteSize(size)
182+
183+
return nil
184+
}

internal/pkg/config/config_test.go

Lines changed: 76 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,10 @@ import (
1111
"testing"
1212
"time"
1313

14+
"github.com/docker/go-units"
1415
"github.com/go-playground/validator/v10"
1516
"github.com/stretchr/testify/assert"
17+
"github.com/stretchr/testify/require"
1618

1719
"github.com/hedhyw/json-log-viewer/internal/pkg/config"
1820
"github.com/hedhyw/json-log-viewer/internal/pkg/tests"
@@ -142,7 +144,7 @@ func ExampleGetDefaultConfig() {
142144
// "50": "error",
143145
// "60": "fatal"
144146
// },
145-
// "maxFileSizeBytes": 1073741824
147+
// "maxFileSizeBytes": 2000000000
146148
// }
147149
}
148150

@@ -299,3 +301,76 @@ func TestValidateField(t *testing.T) {
299301
})
300302
}
301303
}
304+
305+
func TestByteSize(t *testing.T) {
306+
t.Parallel()
307+
308+
testCases := [...]struct {
309+
Value string
310+
Expected config.ByteSize
311+
}{{
312+
Value: `"1k"`,
313+
Expected: units.KB,
314+
}, {
315+
Value: `"1m"`,
316+
Expected: units.MB,
317+
}, {
318+
Value: `"1.5m"`,
319+
Expected: units.MB * 1.5,
320+
}, {
321+
Value: `"1g"`,
322+
Expected: units.GB,
323+
}, {
324+
Value: `"1t"`,
325+
Expected: units.TB,
326+
}, {
327+
Value: `"1p"`,
328+
Expected: units.PB,
329+
}, {
330+
Value: "1",
331+
Expected: 1,
332+
}, {
333+
Value: "12345",
334+
Expected: 12345,
335+
}}
336+
337+
for _, testCase := range testCases {
338+
var actual config.ByteSize
339+
340+
err := json.Unmarshal([]byte(testCase.Value), &actual)
341+
if assert.NoError(t, err, testCase.Value) {
342+
assert.Equal(t, testCase.Expected, actual, testCase.Value)
343+
}
344+
}
345+
}
346+
347+
func TestByteSizeParseFailed(t *testing.T) {
348+
t.Parallel()
349+
350+
t.Run("invalid_number", func(t *testing.T) {
351+
t.Parallel()
352+
353+
var value config.ByteSize
354+
355+
err := json.Unmarshal([]byte(`"123.123.123"`), &value)
356+
require.Error(t, err)
357+
})
358+
359+
t.Run("invalid_suffix", func(t *testing.T) {
360+
t.Parallel()
361+
362+
var value config.ByteSize
363+
364+
err := json.Unmarshal([]byte(`"123X"`), &value)
365+
require.Error(t, err)
366+
})
367+
368+
t.Run("empty", func(t *testing.T) {
369+
t.Parallel()
370+
371+
var value config.ByteSize
372+
373+
err := json.Unmarshal([]byte(`""`), &value)
374+
require.Error(t, err)
375+
})
376+
}

internal/pkg/source/source.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ func File(name string, cfg *config.Config) (*Source, error) {
6666
var err error
6767

6868
source := &Source{
69-
maxSize: cfg.MaxFileSizeBytes,
69+
maxSize: int64(cfg.MaxFileSizeBytes),
7070
name: name,
7171
}
7272

@@ -94,7 +94,7 @@ func Reader(input io.Reader, cfg *config.Config) (*Source, error) {
9494
var err error
9595

9696
source := &Source{
97-
maxSize: cfg.MaxFileSizeBytes,
97+
maxSize: int64(cfg.MaxFileSizeBytes),
9898
}
9999

100100
// We will write the as read to a temp file. Seek against the temp file.

0 commit comments

Comments
 (0)