Skip to content

Commit c5d0ed8

Browse files
committed
Merge branch 'main' into filtering
# Conflicts: # klog/app/cli/args/filter.go # klog/app/cli/args/now.go # klog/app/cli/args/warn.go # klog/service/warning.go
2 parents fc5103e + 5633ed8 commit c5d0ed8

File tree

9 files changed

+108
-95
lines changed

9 files changed

+108
-95
lines changed

klog/app/cli/args/filter.go

Lines changed: 18 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -11,31 +11,31 @@ import (
1111

1212
type FilterArgs struct {
1313
// Date-related filters:
14-
Date klog.Date `name:"date" placeholder:"DATE" group:"Filter" help:"Entries at this date. DATE has to be in format YYYY-MM-DD or YYYY/MM/DD. E.g., '2024-01-31' or '2024/01/31'."`
15-
Since klog.Date `name:"since" placeholder:"DATE" group:"Filter" help:"Entries since this date (inclusive)."`
16-
Until klog.Date `name:"until" placeholder:"DATE" group:"Filter" help:"Entries until this date (inclusive)."`
17-
Period period.Period `name:"period" placeholder:"PERIOD" group:"Filter" help:"Entries within a calendar period. PERIOD has to be in format YYYY, YYYY-MM, YYYY-Www or YYYY-Qq. E.g., '2024', '2024-04', '2022-W21' or '2024-Q1'."`
14+
Date klog.Date `name:"date" placeholder:"DATE" group:"Filter Flags:" help:"Entries at this date. DATE has to be in format YYYY-MM-DD or YYYY/MM/DD. E.g., '2024-01-31' or '2024/01/31'."`
15+
Since klog.Date `name:"since" placeholder:"DATE" group:"Filter Flags:" help:"Entries since this date (inclusive)."`
16+
Until klog.Date `name:"until" placeholder:"DATE" group:"Filter Flags:" help:"Entries until this date (inclusive)."`
17+
Period period.Period `name:"period" placeholder:"PERIOD" group:"Filter Flags:" help:"Entries within a calendar period. PERIOD has to be in format YYYY, YYYY-MM, YYYY-Www or YYYY-Qq. E.g., '2024', '2024-04', '2022-W21' or '2024-Q1'."`
1818

1919
// Filter shortcuts:
2020
// The two `XXX` ones are dummy entries just for the help output, they also aren’t available
2121
// for tab completion. The other ones are not shown in the help output (because that would be
2222
// too verbose then), but they are still available for tab completion.
23-
Today bool `name:"today" group:"Filter" help:"Records at today’s date."`
24-
Yesterday bool `name:"yesterday" group:"Filter" help:"Records at yesterday’s date."`
25-
ThisXXX bool `name:"this-***" group:"Filter" help:"Records of this week/month/quarter/year, e.g. '--this-week' or '--this-quarter'." completion-enabled:"false"`
26-
LastXXX bool `name:"last-***" group:"Filter" help:"Records of last week/month/quarter/year, e.g. '--last-month' or '--last-year'." completion-enabled:"false"`
27-
ThisWeek bool `hidden:"" name:"this-week" group:"Filter" completion-enabled:"true"`
28-
LastWeek bool `hidden:"" name:"last-week" group:"Filter" completion-enabled:"true"`
29-
ThisMonth bool `hidden:"" name:"this-month" group:"Filter" completion-enabled:"true"`
30-
LastMonth bool `hidden:"" name:"last-month" group:"Filter" completion-enabled:"true"`
31-
ThisQuarter bool `hidden:"" name:"this-quarter" group:"Filter" completion-enabled:"true"`
32-
LastQuarter bool `hidden:"" name:"last-quarter" group:"Filter" completion-enabled:"true"`
33-
ThisYear bool `hidden:"" name:"this-year" group:"Filter" completion-enabled:"true"`
34-
LastYear bool `hidden:"" name:"last-year" group:"Filter" completion-enabled:"true"`
23+
Today bool `name:"today" group:"Filter Flags:" help:"Records at today’s date."`
24+
Yesterday bool `name:"yesterday" group:"Filter Flags:" help:"Records at yesterday’s date."`
25+
ThisXXX bool `name:"this-***" group:"Filter Flags:" help:"Records of this week/month/quarter/year, e.g. '--this-week' or '--this-quarter'." completion-enabled:"false"`
26+
LastXXX bool `name:"last-***" group:"Filter Flags:" help:"Records of last week/month/quarter/year, e.g. '--last-month' or '--last-year'." completion-enabled:"false"`
27+
ThisWeek bool `hidden:"" name:"this-week" group:"Filter Flags:" completion-enabled:"true"`
28+
LastWeek bool `hidden:"" name:"last-week" group:"Filter Flags:" completion-enabled:"true"`
29+
ThisMonth bool `hidden:"" name:"this-month" group:"Filter Flags:" completion-enabled:"true"`
30+
LastMonth bool `hidden:"" name:"last-month" group:"Filter Flags:" completion-enabled:"true"`
31+
ThisQuarter bool `hidden:"" name:"this-quarter" group:"Filter Flags:" completion-enabled:"true"`
32+
LastQuarter bool `hidden:"" name:"last-quarter" group:"Filter Flags:" completion-enabled:"true"`
33+
ThisYear bool `hidden:"" name:"this-year" group:"Filter Flags:" completion-enabled:"true"`
34+
LastYear bool `hidden:"" name:"last-year" group:"Filter Flags:" completion-enabled:"true"`
3535

3636
// General filters:
37-
Tags []klog.Tag `name:"tag" placeholder:"TAG" group:"Filter" help:"Entries that match these tags (either in the record summary or the entry summary). You can omit the leading '#'."`
38-
Filter string `name:"filter" placeholder:"EXPR" group:"Filter" help:"Entries that match this filter expression. Run 'klog info --filtering' to learn how expressions works."`
37+
Tags []klog.Tag `name:"tag" placeholder:"TAG" group:"Filter Flags:" help:"Entries that match these tags (either in the record summary or the entry summary). You can omit the leading '#'."`
38+
Filter string `name:"filter" placeholder:"EXPR" group:"Filter Flags:" help:"Entries that match this filter expression. Run 'klog info --filtering' to learn how expressions works."`
3939

4040
hasPartialRecordsWithShouldTotal bool // Field only for internal use
4141
}

klog/app/cli/args/warn.go

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,16 +13,22 @@ type WarnArgs struct {
1313

1414
func (args *WarnArgs) PrintWarnings(ctx app.Context, records []klog.Record, additionalWarnings []service.UsageWarning) {
1515
styler, _ := ctx.Serialise()
16+
warnings := args.GatherWarnings(ctx, records, additionalWarnings)
17+
for _, w := range warnings {
18+
ctx.Print(prettify.PrettifyWarning(w, styler))
19+
}
20+
}
21+
22+
func (args *WarnArgs) GatherWarnings(ctx app.Context, records []klog.Record, additionalWarnings []service.UsageWarning) []string {
1623
if args.NoWarn {
17-
return
24+
return nil
1825
}
1926
disabledCheckers := ctx.Config().NoWarnings.UnwrapOr(service.NewDisabledCheckers())
27+
warnings := service.CheckForWarnings(ctx.Now(), records, disabledCheckers)
2028
for _, warn := range additionalWarnings {
2129
if warn != (service.UsageWarning{}) && !disabledCheckers[warn.Name] {
22-
ctx.Print(prettify.PrettifyGeneralWarning(warn.Message, styler))
30+
warnings = append(warnings, warn.Message)
2331
}
2432
}
25-
service.CheckForWarnings(func(w service.Warning) {
26-
ctx.Print(prettify.PrettifyWarning(w, styler))
27-
}, ctx.Now(), records, disabledCheckers)
33+
return warnings
2834
}

klog/app/cli/json.go

Lines changed: 13 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -4,23 +4,29 @@ import (
44
"github.com/jotaen/klog/klog/app"
55
"github.com/jotaen/klog/klog/app/cli/args"
66
"github.com/jotaen/klog/klog/parser/json"
7+
"github.com/jotaen/klog/klog/service"
78
)
89

910
type Json struct {
1011
Pretty bool `name:"pretty" help:"Pretty-print output."`
1112
args.NowArgs
1213
args.FilterArgs
1314
args.SortArgs
15+
args.WarnArgs
1416
args.InputFilesArgs
1517
}
1618

1719
func (opt *Json) Help() string {
1820
return `
19-
The output structure is a JSON object which contains two properties at the top level: 'records' and 'errors'.
20-
If the file is valid, 'records' is an array containing a JSON object for each record, and 'errors' is 'null'.
21-
If the file has syntax errors, 'records' is 'null', and 'errors' contains an array of error objects.
21+
The output structure is a JSON object which contains three properties at the top level: 'records', 'warnings' and 'errors':
2222
23-
The structure of the 'record' and 'error' objects is always uniform and should be self-explanatory.
23+
- If the file is valid, 'records' is an array containing a JSON object for each record, and 'errors' is 'null'.
24+
25+
- If the file is valid, but contains potential problems, 'warnings' is an array of strings with the respective messages.
26+
27+
- If the file has syntax errors, 'records' and 'warnings' are 'null', and 'errors' contains an array of error objects.
28+
29+
The 'record', 'warnings' and 'error' data structures are always uniform and should be self-explanatory.
2430
You can best explore it by running the command with the --pretty flag.
2531
`
2632
}
@@ -30,7 +36,7 @@ func (opt *Json) Run(ctx app.Context) app.Error {
3036
if err != nil {
3137
parserErrs, isParserErr := err.(app.ParserErrors)
3238
if isParserErr {
33-
ctx.Print(json.ToJson(nil, parserErrs.All(), opt.Pretty) + "\n")
39+
ctx.Print(json.ToJson(nil, parserErrs.All(), nil, opt.Pretty) + "\n")
3440
return nil
3541
}
3642
return err
@@ -45,6 +51,7 @@ func (opt *Json) Run(ctx app.Context) app.Error {
4551
return fErr
4652
}
4753
records = opt.ApplySort(records)
48-
ctx.Print(json.ToJson(records, nil, opt.Pretty) + "\n")
54+
warnings := opt.GatherWarnings(ctx, records, []service.UsageWarning{opt.GetWarning()})
55+
ctx.Print(json.ToJson(records, nil, warnings, opt.Pretty) + "\n")
4956
return nil
5057
}

klog/app/cli/prettify/prettifier.go

Lines changed: 2 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,6 @@ import (
66
"strings"
77

88
"github.com/jotaen/klog/klog/app"
9-
"github.com/jotaen/klog/klog/service"
109
"github.com/jotaen/klog/klog/service/filter"
1110
tf "github.com/jotaen/klog/lib/terminalformat"
1211
)
@@ -81,13 +80,8 @@ func PrettifyFilterError(e filter.ParseError, styler tf.Styler) error {
8180
)
8281
}
8382

84-
// PrettifyWarning formats a warning about a record.
85-
func PrettifyWarning(w service.Warning, styler tf.Styler) string {
86-
return PrettifyGeneralWarning(w.Date().ToString()+": "+w.Warning(), styler)
87-
}
88-
89-
// PrettifyGeneralWarning formats a general warning message.
90-
func PrettifyGeneralWarning(message string, styler tf.Styler) string {
83+
// PrettifyWarning formats a warning message.
84+
func PrettifyWarning(message string, styler tf.Styler) string {
9185
result := ""
9286
result += styler.Props(tf.StyleProps{Background: tf.YELLOW, Color: tf.YELLOW}).Format("[")
9387
result += styler.Props(tf.StyleProps{Background: tf.YELLOW, Color: tf.TEXT_INVERSE}).Format("WARNING")

klog/parser/json/serialiser.go

Lines changed: 10 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -6,27 +6,30 @@ package json
66
import (
77
"bytes"
88
"encoding/json"
9+
"sort"
10+
"strings"
11+
912
"github.com/jotaen/klog/klog"
1013
"github.com/jotaen/klog/klog/parser"
1114
"github.com/jotaen/klog/klog/parser/txt"
1215
"github.com/jotaen/klog/klog/service"
13-
"sort"
14-
"strings"
1516
)
1617

1718
// ToJson serialises records into their JSON representation. The output
1819
// structure is RecordView at the top level.
19-
func ToJson(rs []klog.Record, errs []txt.Error, prettyPrint bool) string {
20+
func ToJson(rs []klog.Record, errs []txt.Error, warnings []string, prettyPrint bool) string {
2021
envelop := func() Envelop {
2122
if errs == nil {
2223
return Envelop{
23-
Records: toRecordViews(rs),
24-
Errors: nil,
24+
Records: toRecordViews(rs),
25+
Warnings: warnings,
26+
Errors: nil,
2527
}
2628
} else {
2729
return Envelop{
28-
Records: nil,
29-
Errors: toErrorViews(errs),
30+
Records: nil,
31+
Warnings: nil,
32+
Errors: toErrorViews(errs),
3033
}
3134
}
3235
}()

klog/parser/json/serialiser_test.go

Lines changed: 35 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,27 +1,29 @@
11
package json
22

33
import (
4+
"testing"
5+
46
"github.com/jotaen/klog/klog"
57
"github.com/jotaen/klog/klog/parser"
68
"github.com/jotaen/klog/klog/parser/txt"
79
"github.com/stretchr/testify/assert"
8-
"testing"
910
)
1011

1112
func TestSerialiseEmptyRecords(t *testing.T) {
12-
json := ToJson([]klog.Record{}, nil, false)
13-
assert.Equal(t, `{"records":[],"errors":null}`, json)
13+
json := ToJson([]klog.Record{}, nil, nil, false)
14+
assert.Equal(t, `{"records":[],"warnings":null,"errors":null}`, json)
1415
}
1516

1617
func TestSerialiseEmptyArrayIfNoErrors(t *testing.T) {
17-
json := ToJson(nil, nil, false)
18-
assert.Equal(t, `{"records":[],"errors":null}`, json)
18+
json := ToJson(nil, nil, nil, false)
19+
assert.Equal(t, `{"records":[],"warnings":null,"errors":null}`, json)
1920
}
2021

2122
func TestSerialisePrettyPrinted(t *testing.T) {
22-
json := ToJson(nil, nil, true)
23+
json := ToJson(nil, nil, nil, true)
2324
assert.Equal(t, `{
2425
"records": [],
26+
"warnings": null,
2527
"errors": null
2628
}`, json)
2729
}
@@ -30,7 +32,7 @@ func TestSerialiseMinimalRecord(t *testing.T) {
3032
json := ToJson(func() []klog.Record {
3133
r := klog.NewRecord(klog.Ɀ_Date_(2000, 12, 31))
3234
return []klog.Record{r}
33-
}(), nil, false)
35+
}(), nil, nil, false)
3436
assert.Equal(t, `{"records":[{`+
3537
`"date":"2000-12-31",`+
3638
`"summary":"",`+
@@ -42,7 +44,7 @@ func TestSerialiseMinimalRecord(t *testing.T) {
4244
`"diff_mins":0,`+
4345
`"tags":[],`+
4446
`"entries":[]`+
45-
`}],"errors":null}`, json)
47+
`}],"warnings":null,"errors":null}`, json)
4648
}
4749

4850
func TestSerialiseFullBlownRecord(t *testing.T) {
@@ -54,7 +56,7 @@ func TestSerialiseFullBlownRecord(t *testing.T) {
5456
r.AddRange(klog.Ɀ_Range_(klog.Ɀ_TimeYesterday_(23, 44), klog.Ɀ_Time_(5, 23)), nil)
5557
r.Start(klog.NewOpenRange(klog.Ɀ_TimeTomorrow_(0, 28)), klog.Ɀ_EntrySummary_("Started #todo=nr4", "still on #it"))
5658
return []klog.Record{r}
57-
}(), nil, false)
59+
}(), nil, nil, false)
5860
assert.Equal(t, `{"records":[{`+
5961
`"date":"2000-12-31",`+
6062
`"summary":"Hello #World\nWhat’s up?",`+
@@ -90,16 +92,16 @@ func TestSerialiseFullBlownRecord(t *testing.T) {
9092
`"start":"0:28>",`+
9193
`"start_mins":1468`+
9294
`}]`+
93-
`}],"errors":null}`, json)
95+
`}],"warnings":null,"errors":null}`, json)
9496
}
9597

9698
func TestSerialiseParserErrors(t *testing.T) {
9799
block, _ := txt.ParseBlock("2018-99-99\n asdf", 6)
98100
json := ToJson(nil, []txt.Error{
99101
parser.ErrorInvalidDate().New(block, 0, 0, 10),
100102
parser.ErrorMalformedSummary().New(block, 1, 3, 5).SetOrigin("/a/b/c/file.klg"),
101-
}, false)
102-
assert.Equal(t, `{"records":null,"errors":[{`+
103+
}, nil, false)
104+
assert.Equal(t, `{"records":null,"warnings":null,"errors":[{`+
103105
`"line":7,`+
104106
`"column":1,`+
105107
`"length":10,`+
@@ -115,3 +117,24 @@ func TestSerialiseParserErrors(t *testing.T) {
115117
`"file":"/a/b/c/file.klg"`+
116118
`}]}`, json)
117119
}
120+
121+
func TestSerialiseWarnings(t *testing.T) {
122+
t.Run("include warnings if records are ok", func(t *testing.T) {
123+
json := ToJson(nil, nil, []string{"Caution!", "Beware!"}, false)
124+
assert.Equal(t, `{"records":[],"warnings":["Caution!","Beware!"],"errors":null}`, json)
125+
})
126+
t.Run("ignore warnings if records are not ok", func(t *testing.T) {
127+
block, _ := txt.ParseBlock("2018-99-99", 6)
128+
json := ToJson(nil, []txt.Error{
129+
parser.ErrorInvalidDate().New(block, 0, 0, 10),
130+
}, []string{"Caution!", "Beware!"}, false)
131+
assert.Equal(t, `{"records":null,"warnings":null,"errors":[{`+
132+
`"line":7,`+
133+
`"column":1,`+
134+
`"length":10,`+
135+
`"title":"Invalid date",`+
136+
`"details":"Please make sure that the date format is either YYYY-MM-DD or YYYY/MM/DD, and that its value represents a valid day in the calendar.",`+
137+
`"file":""`+
138+
`}]}`, json)
139+
})
140+
}

klog/parser/json/view.go

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
package json
22

33
// Envelop is the top level data structure of the JSON output.
4-
// It contains two nodes, `records` and `errors`, one of which is always `null`.
4+
// It contains three nodes:
5+
// - `records`: is `null` if there are errors
6+
// - `warnings`: only if applicable and only unless there are errors
7+
// - `errors`: only unless there are records
58
type Envelop struct {
6-
Records []RecordView `json:"records"`
7-
Errors []ErrorView `json:"errors"`
9+
Records []RecordView `json:"records"`
10+
Warnings []string `json:"warnings"`
11+
Errors []ErrorView `json:"errors"`
812
}
913

1014
// RecordView is the JSON representation of a record.

klog/service/warning.go

Lines changed: 4 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,6 @@ import (
77
"github.com/jotaen/klog/klog"
88
)
99

10-
// Warning contains information for helping locate an issue within the data.
11-
type Warning struct {
12-
date klog.Date
13-
origin checker
14-
}
15-
1610
// UsageWarning contains information for avoiding potential usage issues.
1711
type UsageWarning struct {
1812
Name string
@@ -30,16 +24,6 @@ var (
3024
}
3125
)
3226

33-
// Date is the date of the record that the warning refers to.
34-
func (w Warning) Date() klog.Date {
35-
return w.date
36-
}
37-
38-
// Warning is a short description of the problem.
39-
func (w Warning) Warning() string {
40-
return w.origin.Message()
41-
}
42-
4327
type checker interface {
4428
Warn(klog.Record) klog.Date
4529
Message() string
@@ -66,7 +50,7 @@ func NewDisabledCheckers() DisabledCheckers {
6650
// strict validation, but the main purpose is to help users spot accidental mistakes users
6751
// might have made. The checks are limited to record-level, because otherwise it would
6852
// need to make assumptions on how records are organised within or across files.
69-
func CheckForWarnings(onWarn func(Warning), reference gotime.Time, rs []klog.Record, disabledCheckers DisabledCheckers) {
53+
func CheckForWarnings(reference gotime.Time, rs []klog.Record, disabledCheckers DisabledCheckers) []string {
7054
now := NewDateTimeFromGo(reference)
7155
sortedRs := Sort(rs, false)
7256
checkers := []checker{
@@ -75,20 +59,19 @@ func CheckForWarnings(onWarn func(Warning), reference gotime.Time, rs []klog.Rec
7559
&overlappingTimeRangesChecker{},
7660
&moreThan24HoursChecker{},
7761
}
62+
var warnings []string
7863
for _, r := range sortedRs {
7964
for _, c := range checkers {
8065
if disabledCheckers[c.Name()] {
8166
continue
8267
}
8368
d := c.Warn(r)
8469
if d != nil {
85-
onWarn(Warning{
86-
date: d,
87-
origin: c,
88-
})
70+
warnings = append(warnings, d.ToString()+": "+c.Message())
8971
}
9072
}
9173
}
74+
return warnings
9275
}
9376

9477
type unclosedOpenRangeChecker struct {

0 commit comments

Comments
 (0)