package parse import ( "encoding/json" "errors" "fmt" "strconv" "strings" "github.com/inancgumus/learngo/logparser/v5/pipe" ) const fieldsLength = 4 // record stores fields of a log line. type record struct { Domain string Page string Visits int Uniques int } // Str gets a string field by name. func (r record) Str(field string) string { switch field { case "domain": return r.Domain case "page": return r.Page } panic(fieldErr(field)) } // Int gets an integer field by name. func (r record) Int(field string) int { switch field { case "visits": return r.Visits case "uniques": return r.Uniques } panic(fieldErr(field)) } // Sum the numeric fields with another record. func (r record) Sum(other pipe.Record) pipe.Record { if other == nil { return r } r.Visits += other.(record).Visits r.Uniques += other.(record).Uniques return r } // UnmarshalText to a *record. func (r *record) UnmarshalText(p []byte) (err error) { fields := strings.Fields(string(p)) if len(fields) != fieldsLength { return fmt.Errorf("wrong number of fields %q", fields) } r.Domain, r.Page = fields[0], fields[1] if r.Visits, err = parseStr("visits", fields[2]); err != nil { return err } if r.Uniques, err = parseStr("uniques", fields[3]); err != nil { return err } return validate(*r) } // UnmarshalJSON to a *record. func (r *record) UnmarshalJSON(data []byte) error { // `methodless` doesn't have any methods including UnmarshalJSON. // This trick prevents the stack-overflow (infinite loop). type methodless record var m methodless if err := json.Unmarshal(data, &m); err != nil { return err } // Cast back to the record and save. *r = record(m) return validate(*r) } // parseStr helps UnmarshalText for string to positive int parsing. func parseStr(name, v string) (int, error) { n, err := strconv.Atoi(v) if err != nil { return 0, fmt.Errorf("Record.UnmarshalText %q: %v", name, err) } return n, nil } // validate whether a parsed record is valid or not. func validate(r record) (err error) { switch { case r.Domain == "": err = errors.New("record.domain cannot be empty") case r.Page == "": err = errors.New("record.page cannot be empty") case r.Visits < 0: err = errors.New("record.visits cannot be negative") case r.Uniques < 0: err = errors.New("record.uniques cannot be negative") } return } func fieldErr(field string) error { return fmt.Errorf("record field: %q does not exist", field) }