update: directory structure
This commit is contained in:
		@@ -0,0 +1,14 @@
 | 
			
		||||
### PROBLEM
 | 
			
		||||
+ `parser.go/parse()` also does updating. back to square one.
 | 
			
		||||
  + we need to extract the reusable behavior: scanning.
 | 
			
		||||
 | 
			
		||||
+ inflexible:
 | 
			
		||||
  + adding a filter is hard. needs to change the `scan()` code.
 | 
			
		||||
  + adding a grouper is also hard. domain grouping is hardcoded.
 | 
			
		||||
 | 
			
		||||
## SOLUTION
 | 
			
		||||
+ 
 | 
			
		||||
 | 
			
		||||
## IDEAS:
 | 
			
		||||
 | 
			
		||||
+ make domain filter accept variadic args
 | 
			
		||||
@@ -0,0 +1,36 @@
 | 
			
		||||
// 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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
/*
 | 
			
		||||
p := pipeline{
 | 
			
		||||
	read:     textReader(os.Stdin),
 | 
			
		||||
	write:    textWriter(os.Stdout),
 | 
			
		||||
	filterBy: notUsing(domainExtFilter("io")),
 | 
			
		||||
	groupBy:  domainGrouper,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
if err := start(p); err != nil {
 | 
			
		||||
	fmt.Println("> Err:", err)
 | 
			
		||||
}
 | 
			
		||||
*/
 | 
			
		||||
 | 
			
		||||
func main() {
 | 
			
		||||
	p := newParser()
 | 
			
		||||
 | 
			
		||||
	if err := parse(p); err != nil {
 | 
			
		||||
		fmt.Println("> Err:", err)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	summarize(p)
 | 
			
		||||
}
 | 
			
		||||
@@ -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
 | 
			
		||||
 | 
			
		||||
// parser keeps track of the parsing
 | 
			
		||||
type parser struct {
 | 
			
		||||
	sum     map[string]result // metrics per domain
 | 
			
		||||
	domains []string          // unique domain names
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newParser constructs, initializes and returns a new parser
 | 
			
		||||
func newParser() *parser {
 | 
			
		||||
	return &parser{sum: make(map[string]result)}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parse all the log lines and update the results
 | 
			
		||||
func parse(p *parser) error {
 | 
			
		||||
	process := func(r result) {
 | 
			
		||||
		update(p, r)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err := scan(process)
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func update(p *parser, r result) {
 | 
			
		||||
	// Collect the unique domains
 | 
			
		||||
	if _, ok := p.sum[r.domain]; !ok {
 | 
			
		||||
		p.domains = append(p.domains, r.domain)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// create and assign a new copy of `visit`
 | 
			
		||||
	p.sum[r.domain] = addResult(r, p.sum[r.domain])
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,53 @@
 | 
			
		||||
// 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"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const fieldsLength = 4
 | 
			
		||||
 | 
			
		||||
// result stores the parsed result for a domain
 | 
			
		||||
type result struct {
 | 
			
		||||
	domain, page    string
 | 
			
		||||
	visits, uniques int
 | 
			
		||||
	// add more metrics if needed
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// parseResult from a log line
 | 
			
		||||
func parseResult(line string) (r result, err error) {
 | 
			
		||||
	fields := strings.Fields(line)
 | 
			
		||||
	if len(fields) != fieldsLength {
 | 
			
		||||
		return r, fmt.Errorf("wrong input: %v", fields)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.domain = fields[0]
 | 
			
		||||
	r.page = fields[1]
 | 
			
		||||
 | 
			
		||||
	r.visits, err = strconv.Atoi(fields[2])
 | 
			
		||||
	if err != nil || r.visits < 0 {
 | 
			
		||||
		return r, fmt.Errorf("wrong input: %q", fields[2])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r.uniques, err = strconv.Atoi(fields[3])
 | 
			
		||||
	if err != nil || r.uniques < 0 {
 | 
			
		||||
		return r, fmt.Errorf("wrong input: %q", fields[3])
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// addResult to another one
 | 
			
		||||
func addResult(r, other result) result {
 | 
			
		||||
	r.visits += other.visits
 | 
			
		||||
	r.uniques += other.uniques
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,35 @@
 | 
			
		||||
// 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"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"os"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type processFn func(r result)
 | 
			
		||||
 | 
			
		||||
func scan(process processFn) error {
 | 
			
		||||
	var (
 | 
			
		||||
		l  = 1
 | 
			
		||||
		in = bufio.NewScanner(os.Stdin)
 | 
			
		||||
	)
 | 
			
		||||
 | 
			
		||||
	for in.Scan() {
 | 
			
		||||
		r, err := parseResult(in.Text())
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("line %d: %v", l, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		l++
 | 
			
		||||
 | 
			
		||||
		process(r)
 | 
			
		||||
	}
 | 
			
		||||
	return in.Err()
 | 
			
		||||
}
 | 
			
		||||
@@ -0,0 +1,48 @@
 | 
			
		||||
// 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 summarizes and prints the parsing result
 | 
			
		||||
// + violation: accesses the parsing internals: p.domains + p.sum + p.total
 | 
			
		||||
// + give it the []result only.
 | 
			
		||||
// + let it calculate the total.
 | 
			
		||||
const (
 | 
			
		||||
 | 
			
		||||
	//          DOMAINS PAGES VISITS UNIQUES
 | 
			
		||||
	//             ^      ^     ^    ^
 | 
			
		||||
	//             |      |     |    |
 | 
			
		||||
	header     = "%-25s %-10s %10s %10s\n"
 | 
			
		||||
	line       = "%-25s %-10s %10d %10d\n"
 | 
			
		||||
	footer     = "\n%-36s %10d %10d\n" // -> "" VISITS UNIQUES
 | 
			
		||||
	dash       = "-"
 | 
			
		||||
	dashLength = 58
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// summarize summarizes and prints the parsing result
 | 
			
		||||
func summarize(p *parser) {
 | 
			
		||||
	sort.Strings(p.domains)
 | 
			
		||||
 | 
			
		||||
	fmt.Printf(header, "DOMAIN", "PAGES", "VISITS", "UNIQUES")
 | 
			
		||||
	fmt.Println(strings.Repeat("-", dashLength))
 | 
			
		||||
 | 
			
		||||
	var total result
 | 
			
		||||
 | 
			
		||||
	for _, domain := range p.domains {
 | 
			
		||||
		r := p.sum[domain]
 | 
			
		||||
		total = addResult(total, r)
 | 
			
		||||
 | 
			
		||||
		fmt.Printf(line, r.domain, r.page, r.visits, r.uniques)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf(footer, "TOTAL", total.visits, total.uniques)
 | 
			
		||||
}
 | 
			
		||||
		Reference in New Issue
	
	Block a user