From 8053066a6b584129339cfd3ee6acee6f0395e823 Mon Sep 17 00:00:00 2001 From: Inanc Gumus Date: Fri, 26 Apr 2019 07:11:36 +0300 Subject: [PATCH] add: log parser for methods --- .../2-methods/xxx-log-parser-methods/log.txt | 6 +++ .../log_err_missing.txt | 6 +++ .../log_err_negative.txt | 6 +++ .../xxx-log-parser-methods/log_err_str.txt | 6 +++ .../2-methods/xxx-log-parser-methods/main.go | 24 +++++++++ .../xxx-log-parser-methods/parser.go | 35 +++++++++++++ .../xxx-log-parser-methods/report.go | 39 ++++++++++++++ .../xxx-log-parser-methods/result.go | 51 +++++++++++++++++++ .../xxx-log-parser-methods/summarize.go | 40 +++++++++++++++ 9 files changed, 213 insertions(+) create mode 100644 x-tba/2-methods/xxx-log-parser-methods/log.txt create mode 100644 x-tba/2-methods/xxx-log-parser-methods/log_err_missing.txt create mode 100644 x-tba/2-methods/xxx-log-parser-methods/log_err_negative.txt create mode 100644 x-tba/2-methods/xxx-log-parser-methods/log_err_str.txt create mode 100644 x-tba/2-methods/xxx-log-parser-methods/main.go create mode 100644 x-tba/2-methods/xxx-log-parser-methods/parser.go create mode 100644 x-tba/2-methods/xxx-log-parser-methods/report.go create mode 100644 x-tba/2-methods/xxx-log-parser-methods/result.go create mode 100644 x-tba/2-methods/xxx-log-parser-methods/summarize.go diff --git a/x-tba/2-methods/xxx-log-parser-methods/log.txt b/x-tba/2-methods/xxx-log-parser-methods/log.txt new file mode 100644 index 0000000..fb2432b --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/log.txt @@ -0,0 +1,6 @@ +learngoprogramming.com 10 +learngoprogramming.com 10 +golang.org 4 +golang.org 6 +blog.golang.org 20 +blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/2-methods/xxx-log-parser-methods/log_err_missing.txt b/x-tba/2-methods/xxx-log-parser-methods/log_err_missing.txt new file mode 100644 index 0000000..fd8eff4 --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/log_err_missing.txt @@ -0,0 +1,6 @@ +learngoprogramming.com 10 +learngoprogramming.com 10 +golang.org +golang.org 6 +blog.golang.org 20 +blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/2-methods/xxx-log-parser-methods/log_err_negative.txt b/x-tba/2-methods/xxx-log-parser-methods/log_err_negative.txt new file mode 100644 index 0000000..60485c0 --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/log_err_negative.txt @@ -0,0 +1,6 @@ +learngoprogramming.com 10 +learngoprogramming.com 10 +golang.org -100 +golang.org 6 +blog.golang.org 20 +blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/2-methods/xxx-log-parser-methods/log_err_str.txt b/x-tba/2-methods/xxx-log-parser-methods/log_err_str.txt new file mode 100644 index 0000000..3a55bd7 --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/log_err_str.txt @@ -0,0 +1,6 @@ +learngoprogramming.com 10 +learngoprogramming.com 10 +golang.org FOUR +golang.org 6 +blog.golang.org 20 +blog.golang.org 10 \ No newline at end of file diff --git a/x-tba/2-methods/xxx-log-parser-methods/main.go b/x-tba/2-methods/xxx-log-parser-methods/main.go new file mode 100644 index 0000000..f96a92f --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/main.go @@ -0,0 +1,24 @@ +// 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 ( + "bufio" + "os" +) + +func main() { + in := bufio.NewScanner(os.Stdin) + + p, report := new(parser), newReport() + for in.Scan() { + report.update(p.parse(in.Text())) + } + + summarize(report, p.lerr, in.Err()) +} diff --git a/x-tba/2-methods/xxx-log-parser-methods/parser.go b/x-tba/2-methods/xxx-log-parser-methods/parser.go new file mode 100644 index 0000000..f8419e7 --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/parser.go @@ -0,0 +1,35 @@ +package main + +import ( + "fmt" +) + +// parser keep tracks of the parsing +type parser struct { + lines int // number of parsed lines (for the error messages) + lerr error // the last error occurred +} + +// parserResult wraps a result for generating parser error +type parserResult struct { + result // use struct embedding + err error // inject an error +} + +// parse parses a log line and returns a result with an injected error +func (p *parser) parse(line string) (parsed parserResult) { + // always set the error + defer func() { parsed.err = p.lerr }() + + // if there was an error do not continue + if p.lerr != nil { + return + } + + // chain the parser's error to the result's + res, err := parseLine(line) + if p.lines++; err != nil { + p.lerr = fmt.Errorf("%s: (line #%d)", err, p.lines) + } + return parserResult{result: res} +} diff --git a/x-tba/2-methods/xxx-log-parser-methods/report.go b/x-tba/2-methods/xxx-log-parser-methods/report.go new file mode 100644 index 0000000..ee3de75 --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/report.go @@ -0,0 +1,39 @@ +// 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 + +// report aggregates the final report +type report struct { + sum map[string]result // metrics per domain + domains []string // unique domain names + total result // total visits for all domains +} + +// newReport constructs and initializes a new report +func newReport() *report { + return &report{sum: make(map[string]result)} +} + +// update updates the errors for the given parsing result +func (r *report) update(parsed parserResult) { + // do not update the report if the result has an error + if parsed.err != nil { + return + } + + domain := parsed.domain + if _, ok := r.sum[domain]; !ok { + r.domains = append(r.domains, domain) + } + + // let the result handle the addition + // this allows us to manage the result in once place + // and this way it becomes easily extendable + r.total = r.total.add(parsed.result) + r.sum[domain] = parsed.add(r.sum[domain]) +} diff --git a/x-tba/2-methods/xxx-log-parser-methods/result.go b/x-tba/2-methods/xxx-log-parser-methods/result.go new file mode 100644 index 0000000..097e22f --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/result.go @@ -0,0 +1,51 @@ +// 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 ( + "fmt" + "strconv" + "strings" +) + +// always put all the related things together as in here + +// result stores metrics for a domain +// it uses the value mechanics, +// because it doesn't have to update anything +type result struct { + domain string + visits int + // add more metrics if needed +} + +// add adds the metrics of another result to itself and returns a new result +func (r result) add(other result) result { + return result{ + domain: r.domain, + visits: r.visits + other.visits, + } +} + +// parseLine parses a single result line +func parseLine(line string) (parsed result, err error) { + fields := strings.Fields(line) + if len(fields) != 2 { + err = fmt.Errorf("wrong input: %v", fields) + return + } + + parsed.domain = fields[0] + + parsed.visits, err = strconv.Atoi(fields[1]) + if parsed.visits < 0 || err != nil { + err = fmt.Errorf("wrong input: %q", fields[1]) + } + + return +} diff --git a/x-tba/2-methods/xxx-log-parser-methods/summarize.go b/x-tba/2-methods/xxx-log-parser-methods/summarize.go new file mode 100644 index 0000000..4fbf125 --- /dev/null +++ b/x-tba/2-methods/xxx-log-parser-methods/summarize.go @@ -0,0 +1,40 @@ +// 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 ( + "fmt" + "sort" + "strings" +) + +// summarize prints the report and errors if any +func summarize(r *report, errs ...error) { + sort.Strings(r.domains) + + fmt.Printf("%-30s %10s\n", "DOMAIN", "VISITS") + fmt.Println(strings.Repeat("-", 45)) + + for _, domain := range r.domains { + parsed := r.sum[domain] + fmt.Printf("%-30s %10d\n", domain, parsed.visits) + } + fmt.Printf("\n%-30s %10d\n", "TOTAL", r.total.visits) + + // only handle the errors once + dumpErrs(errs...) +} + +// this variadic func simplifies the multiple error handling +func dumpErrs(errs ...error) { + for _, err := range errs { + if err != nil { + fmt.Printf("> Err: %s\n", err) + } + } +}