refactor: oop log parser

This commit is contained in:
Inanc Gumus
2019-08-26 14:37:58 +03:00
parent 912d789933
commit 5215300d31
8 changed files with 136 additions and 126 deletions

View File

@ -10,8 +10,8 @@ package main
import "sort"
type analysis struct {
sum map[string]result // metrics per domain
keys []string // unique keys
sum map[string]result // metrics per group key
keys []string // unique group keys
groupKey groupFunc
filter filterFunc
}
@ -24,6 +24,31 @@ func newAnalysis() *analysis {
}
}
// transform the result
func (a *analysis) transform(r result) {
if !a.filter(r) {
return
}
key := a.groupKey(r)
if _, ok := a.sum[key]; !ok {
a.keys = append(a.keys, key)
}
a.sum[key] = r.add(a.sum[key])
}
// each yields an analysis result
func (a *analysis) each(yield resultFn) error {
sort.Strings(a.keys)
for _, key := range a.keys {
yield(a.sum[key])
}
return nil
}
func (a *analysis) groupBy(g groupFunc) {
if g != nil {
a.groupKey = g
@ -35,26 +60,3 @@ func (a *analysis) filterBy(f filterFunc) {
a.filter = f
}
}
// analyse the given result
func (a *analysis) analyse(r result) {
if !a.filter(r) {
return
}
key := a.groupKey(r)
if _, ok := a.sum[key]; !ok {
a.keys = append(a.keys, key)
}
a.sum[key] = r.add(a.sum[key])
}
// each sends an analysis result to `handle`
func (a *analysis) each(handle resultFn) {
sort.Strings(a.keys)
for _, domain := range a.keys {
handle(a.sum[domain])
}
}

View File

@ -10,12 +10,12 @@ import (
// You need to run:
// go get -u github.com/wcharczuk/go-chart
type chartSummary struct {
type chartReport struct {
title string
width, height int
}
func (s *chartSummary) summarize(results iterator) error {
func (s *chartReport) report(results iterator) error {
w := os.Stdout
donut := c.DonutChart{

View File

@ -1,4 +1,4 @@
// For more tutorials: https://bp.learngoprogramming.com
// For more tutorials: https://bj.learngoprogramming.com
//
// Copyright © 2018 Inanc Gumus
// Learn Go Programming Course
@ -14,26 +14,26 @@ import (
"io/ioutil"
)
type jsonParser struct {
r io.Reader
type jsonLog struct {
reader io.Reader
}
func newJSONParser(r io.Reader) *jsonParser {
return &jsonParser{r: r}
func newJSONLog(r io.Reader) *jsonLog {
return &jsonLog{reader: r}
}
func (p *jsonParser) parse(handle resultFn) error {
defer readClose(p.r)
func (j *jsonLog) each(yield resultFn) error {
defer readClose(j.reader)
bytes, err := ioutil.ReadAll(p.r)
bytes, err := ioutil.ReadAll(j.reader)
if err != nil {
return err
}
return parseJSON(bytes, handle)
return extractJSON(bytes, yield)
}
func parseJSON(bytes []byte, handle resultFn) error {
func extractJSON(bytes []byte, yield resultFn) error {
var rs []struct {
Domain string
Page string
@ -49,7 +49,7 @@ func parseJSON(bytes []byte, handle resultFn) error {
}
for _, r := range rs {
handle(result{
yield(result{
domain: r.Domain,
page: r.Page,
visits: r.Visits,

View File

@ -14,20 +14,23 @@ import (
)
func main() {
a := newAnalysis()
// a.filterBy(notUsing(domainExtFilter("io", "com")))
// a.groupBy(domainGrouper)
an := newAnalysis()
// an.filterBy(notUsing(domainExtFilter("io", "com")))
// an.filterBy(domainFilter("org"))
// an.groupBy(domainGrouper)
p := newTextParser(os.Stdin)
s := newTextSummary()
src := newTextLog(os.Stdin)
dst := newTextReport()
// s := &chartSummary{
// title: "visits per domain",
// width: 1920,
// height: 800,
// }
// s := &chartReport{
// title: "visits per domain",
// width: 1920,
// height: 800,
// }
if err := report(p, a, s); err != nil {
pipe := newPipeline(src, dst, an)
if err := pipe.run(); err != nil {
log.Fatalln(err)
}

View File

@ -0,0 +1,68 @@
// 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 (
"os"
"strings"
)
type resultFn func(result)
type iterator interface {
each(resultFn) error
}
type transformer interface {
transform(result)
iterator
}
type reporter interface {
report(iterator) error
}
type pipeline struct {
src iterator
dst reporter
tran transformer
}
func newPipeline(source iterator, r reporter, t transformer) *pipeline {
return &pipeline{
src: source,
dst: r,
tran: t,
}
}
// fromFile generates a default report
func fromFile(path string) (err error) {
f, err := os.Open(path)
if err != nil {
return err
}
var src iterator
switch {
case strings.HasSuffix(path, ".txt"):
src = newTextLog(f)
case strings.HasSuffix(path, ".json"):
src = newJSONLog(f)
}
p := newPipeline(src, newTextReport(), newAnalysis())
return p.run()
}
func (p *pipeline) run() error {
if err := p.src.each(p.tran.transform); err != nil {
return err
}
return p.dst.report(p.tran)
}

View File

@ -1,63 +0,0 @@
// 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 (
"errors"
"os"
"strings"
)
type resultFn func(result)
type parser interface {
parse(resultFn) error
}
type analyser interface {
analyse(result)
}
type iterator interface {
each(resultFn)
}
type summarizer interface {
summarize(iterator) error
}
func report(p parser, a analyser, s summarizer) error {
if err := p.parse(a.analyse); err != nil {
return err
}
it, ok := a.(iterator)
if !ok {
return errors.New("cannot iterate on analyser")
}
return s.summarize(it)
}
// reportFromFile generates a default report
func reportFromFile(path string) (err error) {
f, err := os.Open(path)
if err != nil {
return err
}
var p parser
switch {
case strings.HasSuffix(path, ".txt"):
p = newTextParser(f)
case strings.HasSuffix(path, ".json"):
p = newJSONParser(f)
}
return report(p, newAnalysis(), newTextSummary())
}

View File

@ -15,36 +15,36 @@ import (
"strings"
)
type textParser struct {
r io.Reader
type textLog struct {
reader io.Reader
}
func newTextParser(r io.Reader) *textParser {
return &textParser{r: r}
func newTextLog(r io.Reader) *textLog {
return &textLog{reader: r}
}
func (p *textParser) parse(handle resultFn) error {
defer readClose(p.r)
func (p *textLog) each(yield resultFn) error {
defer readClose(p.reader)
var (
l = 1
in = bufio.NewScanner(p.r)
in = bufio.NewScanner(p.reader)
)
for in.Scan() {
r, err := parseFields(in.Text())
r, err := extractFields(in.Text())
if err != nil {
return fmt.Errorf("line %d: %v", l, err)
}
handle(r)
yield(r)
l++
}
return in.Err()
}
func parseFields(s string) (r result, err error) {
func extractFields(s string) (r result, err error) {
fields := strings.Fields(s)
if len(fields) != fieldsLength {
return r, fmt.Errorf("wrong number of fields %q", fields)

View File

@ -21,13 +21,13 @@ const (
flags = 0
)
type textSummary struct{}
type textReport struct{}
func newTextSummary() *textSummary {
return new(textSummary)
func newTextReport() *textReport {
return new(textReport)
}
func (s *textSummary) summarize(results iterator) error {
func (s *textReport) report(results iterator) error {
w := tabwriter.NewWriter(os.Stdout, minWidth, tabWidth, padding, ' ', flags)
write := fmt.Fprintf