From 49103708084b086f55e7a069f55ff11485a8a8fc Mon Sep 17 00:00:00 2001 From: Inanc Gumus Date: Thu, 29 Aug 2019 01:32:25 +0300 Subject: [PATCH] add: logparser v5 shared record --- logparser/v5/main.go | 1 + logparser/v5/passthrough.go | 31 +++++++++++++++++ logparser/v5/pipe/filter.go | 1 + logparser/v5/pipe/filters.go | 6 ++-- logparser/v5/pipe/group.go | 1 + logparser/v5/pipe/groupers.go | 4 +-- logparser/v5/pipe/record.go | 30 ++++++++--------- logparser/v5/pipe/recordshare.go | 58 ++++++++++++++++++++++++++++++++ logparser/v5/pipe/textreport.go | 8 ++--- 9 files changed, 116 insertions(+), 24 deletions(-) create mode 100644 logparser/v5/passthrough.go create mode 100644 logparser/v5/pipe/recordshare.go diff --git a/logparser/v5/main.go b/logparser/v5/main.go index fe0b722..aebd436 100644 --- a/logparser/v5/main.go +++ b/logparser/v5/main.go @@ -27,6 +27,7 @@ func main() { // pipe.NewJSONReport(os.Stdout), pipe.FilterBy(pipe.DomainExtFilter("com", "io")), pipe.GroupBy(pipe.DomainGrouper), + // new(passThrough), ) if err := p.Run(); err != nil { diff --git a/logparser/v5/passthrough.go b/logparser/v5/passthrough.go new file mode 100644 index 0000000..4261f73 --- /dev/null +++ b/logparser/v5/passthrough.go @@ -0,0 +1,31 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package main + +import ( + "github.com/inancgumus/learngo/logparser/v5/pipe" +) + +type passThrough struct { + pipe.Iterator +} + +func (t *passThrough) Consume(results pipe.Iterator) error { + t.Iterator = results + return nil +} + +func (t *passThrough) Each(yield func(pipe.Record) error) error { + + return t.Iterator.Each(func(r pipe.Record) error { + // fmt.Println(r.Fields()) + // fmt.Println(r.Int("visits")) + return yield(r) + }) + +} diff --git a/logparser/v5/pipe/filter.go b/logparser/v5/pipe/filter.go index 9ac01a4..ea25bea 100644 --- a/logparser/v5/pipe/filter.go +++ b/logparser/v5/pipe/filter.go @@ -8,6 +8,7 @@ package pipe // FilterFunc represents a filtering pipeline func. +// The type alias frees us from binding to a named type. type FilterFunc = func(Record) (pass bool) // Filter the records. diff --git a/logparser/v5/pipe/filters.go b/logparser/v5/pipe/filters.go index d66d393..da2f4db 100644 --- a/logparser/v5/pipe/filters.go +++ b/logparser/v5/pipe/filters.go @@ -20,7 +20,7 @@ func NotFilter(filter FilterFunc) FilterFunc { func DomainExtFilter(domains ...string) FilterFunc { return func(r Record) bool { for _, domain := range domains { - if strings.HasSuffix(r.Domain, "."+domain) { + if strings.HasSuffix(r.domain, "."+domain) { return true } } @@ -31,11 +31,11 @@ func DomainExtFilter(domains ...string) FilterFunc { // DomainFilter filters a domain if it contains the given text. func DomainFilter(text string) FilterFunc { return func(r Record) bool { - return strings.Contains(r.Domain, text) + return strings.Contains(r.domain, text) } } // DomainOrgFilter filters only the ".org" domains. func DomainOrgFilter(r Record) bool { - return strings.HasSuffix(r.Domain, ".org") + return strings.HasSuffix(r.domain, ".org") } diff --git a/logparser/v5/pipe/group.go b/logparser/v5/pipe/group.go index 5faef61..2eda161 100644 --- a/logparser/v5/pipe/group.go +++ b/logparser/v5/pipe/group.go @@ -12,6 +12,7 @@ import ( ) // GroupFunc represents a grouping func that returns a grouping key. +// The type alias frees us from binding to a named type. type GroupFunc = func(Record) (key string) // Group records by a key. diff --git a/logparser/v5/pipe/groupers.go b/logparser/v5/pipe/groupers.go index 4b3cc59..7c208b2 100644 --- a/logparser/v5/pipe/groupers.go +++ b/logparser/v5/pipe/groupers.go @@ -12,10 +12,10 @@ package pipe // For example: It returns the page field as well. // Exercise: Write a solution that removes the unnecessary data. func DomainGrouper(r Record) string { - return r.Domain + return r.domain } // Page groups records by page. func Page(r Record) string { - return r.Domain + r.Page + return r.domain + r.page } diff --git a/logparser/v5/pipe/record.go b/logparser/v5/pipe/record.go index 3fe016c..1cd1a6a 100644 --- a/logparser/v5/pipe/record.go +++ b/logparser/v5/pipe/record.go @@ -10,18 +10,18 @@ import ( const fieldsLength = 4 -// Record stores fields of a log line. -type Record struct { - Domain string - Page string - Visits int - Uniques int +// record stores fields of a log line. +type record struct { + domain string + page string + visits int + uniques int } // Sum the numeric fields with another record. func (r Record) Sum(other Record) Record { - r.Visits += other.Visits - r.Uniques += other.Uniques + r.visits += other.visits + r.uniques += other.uniques return r } @@ -32,12 +32,12 @@ func (r *Record) UnmarshalText(p []byte) (err error) { return fmt.Errorf("wrong number of fields %q", fields) } - r.Domain, r.Page = fields[0], fields[1] + r.domain, r.page = fields[0], fields[1] - if r.Visits, err = parseStr("visits", fields[2]); err != nil { + if r.visits, err = parseStr("visits", fields[2]); err != nil { return err } - if r.Uniques, err = parseStr("uniques", fields[3]); err != nil { + if r.uniques, err = parseStr("uniques", fields[3]); err != nil { return err } return validate(*r) @@ -72,13 +72,13 @@ func parseStr(name, v string) (int, error) { // validate whether a parsed record is valid or not. func validate(r Record) (err error) { switch { - case r.Domain == "": + case r.domain == "": err = errors.New("record.domain cannot be empty") - case r.Page == "": + case r.page == "": err = errors.New("record.page cannot be empty") - case r.Visits < 0: + case r.visits < 0: err = errors.New("record.visits cannot be negative") - case r.Uniques < 0: + case r.uniques < 0: err = errors.New("record.uniques cannot be negative") } return diff --git a/logparser/v5/pipe/recordshare.go b/logparser/v5/pipe/recordshare.go new file mode 100644 index 0000000..e7c949a --- /dev/null +++ b/logparser/v5/pipe/recordshare.go @@ -0,0 +1,58 @@ +// For more tutorials: https://blog.learngoprogramming.com +// +// Copyright © 2018 Inanc Gumus +// Learn Go Programming Course +// License: https://creativecommons.org/licenses/by-nc-sa/4.0/ +// + +package pipe + +import ( + "fmt" + "reflect" +) + +// Record stores the log line fields. +// The underlying fields are kept hidden. +// The users of the package are decoupled from the underlying record fields. +// When the fields change, the client won't feel the difference (at least in compile-time). +type Record struct { + record +} + +// String returns a string field. Panics when the field doesn't exist. +func (r Record) String(field string) string { + return r.mustGet(field, reflect.String).String() +} + +// Int returns an int field. Panics when the field doesn't exist. +func (r Record) Int(field string) int { + return int(r.mustGet(field, reflect.Int).Int()) +} + +// Fields returns all the field names. +// The names can be used to query the Record. +func (r Record) Fields() (fields []string) { + t := reflect.TypeOf(record{}) + + for i := 0; i < t.NumField(); i++ { + f := t.Field(i) + s := fmt.Sprintf("(%s: %s)", f.Name, f.Type.Name()) + + fields = append(fields, s) + } + + return +} + +// mustGet the field with the same kind or panics. +func (r Record) mustGet(field string, kind reflect.Kind) reflect.Value { + v := reflect.ValueOf(r.record).FieldByName(field) + if !v.IsValid() { + panic(fmt.Errorf("record.%s does not exist", field)) + } + if v.Kind() != kind { + panic(fmt.Errorf("record.%s is not %q", field, kind)) + } + return v +} diff --git a/logparser/v5/pipe/textreport.go b/logparser/v5/pipe/textreport.go index b13925f..0c8c0f0 100644 --- a/logparser/v5/pipe/textreport.go +++ b/logparser/v5/pipe/textreport.go @@ -45,8 +45,8 @@ func (t *TextReport) Consume(records Iterator) error { total = r.Sum(total) write(w, "%s\t%s\t%d\t%d\n", - r.Domain, r.Page, - r.Visits, r.Uniques, + r.domain, r.page, + r.visits, r.uniques, ) return nil @@ -57,8 +57,8 @@ func (t *TextReport) Consume(records Iterator) error { write(w, "\t\t\t\n") write(w, "%s\t%s\t%d\t%d\n", "TOTAL", "", - total.Visits, - total.Uniques, + total.visits, + total.uniques, ) return w.Flush()