Skip to content

Commit f43ec32

Browse files
authored
Merge pull request #8 from nugget/vehiclestats
Add vehiclestats example code
2 parents cff5392 + 860b9f0 commit f43ec32

File tree

10 files changed

+523
-33
lines changed

10 files changed

+523
-33
lines changed

.github/workflows/go.yml

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,6 @@ on:
1010
branches: [ "main" ]
1111

1212
jobs:
13-
1413
build:
1514
runs-on: ubuntu-latest
1615
steps:
@@ -29,3 +28,10 @@ jobs:
2928
working-directory: roadtrip
3029
run: go test -v ./...
3130

31+
- name: Build vehiclestats example
32+
working-directory: examples/vehiclestats
33+
run: go build -v ./...
34+
35+
- name: Test vehiclestats example
36+
working-directory: examples/vehiclestats
37+
run: go test -v ./...
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
name: golangci-lint
2+
on:
3+
push:
4+
branches:
5+
- main
6+
- master
7+
pull_request:
8+
9+
permissions:
10+
contents: read
11+
# Optional: allow read access to pull request. Use with `only-new-issues` option.
12+
# pull-requests: read
13+
14+
jobs:
15+
golangci:
16+
name: lint
17+
runs-on: ubuntu-latest
18+
steps:
19+
- uses: actions/checkout@v4
20+
- uses: actions/setup-go@v5
21+
with:
22+
go-version: stable
23+
- name: golangci-lint on roadtrip package
24+
uses: golangci/golangci-lint-action@v6
25+
with:
26+
version: v1.62.2
27+
working-directory: roadtrip
28+
- name: golangci-lint on vehiclestats example
29+
uses: golangci/golangci-lint-action@v6
30+
with:
31+
version: v1.62.2
32+
working-directory: examples/vehiclestats

.golangci.yml

Lines changed: 365 additions & 0 deletions
Large diffs are not rendered by default.

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
[![Go](https://github.com/nugget/roadtrip/actions/workflows/go.yml/badge.svg)](https://github.com/nugget/roadtrip/actions/workflows/go.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/nugget/roadtrip-go/roadtrip.svg)](https://pkg.go.dev/github.com/nugget/roadtrip-go) [![Go Report Card](https://goreportcard.com/badge/github.com/nugget/roadtrip-go/roadtrip)](https://goreportcard.com/report/github.com/nugget/roadtrip-go/roadtrip)
1+
[![Go](https://github.com/nugget/roadtrip/actions/workflows/go.yml/badge.svg)](https://github.com/nugget/roadtrip/actions/workflows/go.yml) [![Go Reference](https://pkg.go.dev/badge/github.com/nugget/roadtrip-go/roadtrip.svg)](https://pkg.go.dev/github.com/nugget/roadtrip-go/roadtrip) [![Go Report Card](https://goreportcard.com/badge/github.com/nugget/roadtrip-go/roadtrip)](https://goreportcard.com/report/github.com/nugget/roadtrip-go/roadtrip)
22

33
[Road Trip](https://darrensoft.ca/roadtrip/) is an iOS application written by
44
Darren Stone. This Go package provides methods and functions for reading and

examples/vehiclestats/go.mod

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
module github.com/nugget/roadtrip-go/examples/stats
2+
3+
go 1.23.3
4+
5+
require github.com/nugget/roadtrip-go/roadtrip v0.0.0-20241228055138-36e5f41ec9fe
6+
7+
require (
8+
github.com/hashicorp/errwrap v1.1.0 // indirect
9+
github.com/hashicorp/go-multierror v1.1.1 // indirect
10+
github.com/tiendc/go-csvlib v1.0.0 // indirect
11+
github.com/tiendc/go-rflutil v0.0.0-20231112145832-693b7b74d697 // indirect
12+
github.com/tiendc/gofn v1.11.0 // indirect
13+
)
14+
15+
// replace github.com/nugget/roadtrip-go/roadtrip => ../../roadtrip

examples/vehiclestats/go.sum

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
2+
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
3+
github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
4+
github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
5+
github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
6+
github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
7+
github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
8+
github.com/nugget/roadtrip-go/roadtrip v0.0.0-20241228055138-36e5f41ec9fe h1:bEoZ218uEojTZvt0ri2YgN3Iq/5bG2Rh8vpCoP0ub0g=
9+
github.com/nugget/roadtrip-go/roadtrip v0.0.0-20241228055138-36e5f41ec9fe/go.mod h1:wiC7U7pYaY5bll/aNkxlIbJ9GFTGbk1K3k//YJw9r7A=
10+
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
11+
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
12+
github.com/stretchr/testify v1.9.0 h1:HtqpIVDClZ4nwg75+f6Lvsy/wHu+3BoSGCbBAcpTsTg=
13+
github.com/stretchr/testify v1.9.0/go.mod h1:r2ic/lqez/lEtzL7wO/rwa5dbSLXVDPFyf8C91i36aY=
14+
github.com/tiendc/go-csvlib v1.0.0 h1:8jz6zKY617KvYE24xUzl/KLG1C1ni/tDN6nosO2DWrQ=
15+
github.com/tiendc/go-csvlib v1.0.0/go.mod h1:8zM3bjEHozZA/CUFi8OpAMlowPBKfVx7qqyLDfZjSkA=
16+
github.com/tiendc/go-rflutil v0.0.0-20231112145832-693b7b74d697 h1:BYWZUvxBkpnlC4MywWhO1bEch5L6cCc0t5FNVSUjZps=
17+
github.com/tiendc/go-rflutil v0.0.0-20231112145832-693b7b74d697/go.mod h1:nSMBac9C+G4b8nvxSgPZ0rmhrLonJ5ZdknynSKxQhL8=
18+
github.com/tiendc/gofn v1.11.0 h1:rjXJ2tZ6L96ICwBkbVv0ubDOvrGRo1QMXhhq90xZTBw=
19+
github.com/tiendc/gofn v1.11.0/go.mod h1:uevHlES37QrasSvoZxBUcooejk7QfvBCfrJ809b+giA=
20+
gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
21+
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
Lines changed: 45 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,45 @@
1+
package main
2+
3+
import (
4+
"flag"
5+
"fmt"
6+
"log/slog"
7+
"os"
8+
9+
"github.com/nugget/roadtrip-go/roadtrip"
10+
)
11+
12+
func main() {
13+
_ = slog.SetLogLoggerLevel(slog.LevelInfo)
14+
15+
var debugMode = flag.Bool("v", false, "Verbose logging")
16+
var filename = flag.String("file", "", "Road Trip vehicle CSV file")
17+
18+
flag.Parse()
19+
20+
if *debugMode {
21+
_ = slog.SetLogLoggerLevel(slog.LevelDebug)
22+
}
23+
24+
if *filename == "" {
25+
slog.Error("no filename provided (--file)")
26+
os.Exit(1)
27+
}
28+
29+
vehicle, err := roadtrip.NewFromFile(*filename)
30+
if err != nil {
31+
slog.Error("unable to load CSV file", "error", err)
32+
os.Exit(1)
33+
}
34+
35+
// slog.Info("Loaded vehicle", "vehicle", vehicle)
36+
37+
totalFuelCost := 0.00
38+
39+
for i, f := range vehicle.FuelRecords {
40+
slog.Debug("processing FuelRecord", "i", i, "f", f)
41+
totalFuelCost += f.TotalPrice
42+
}
43+
44+
fmt.Printf("Spent %0.02f on fuel in %d fillups\n", totalFuelCost, len(vehicle.FuelRecords))
45+
}

roadtrip/roadtrip.go

Lines changed: 27 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ type Vehicle struct {
2121
Vehicles []VehicleRecord `section:"VEHICLE"`
2222
FuelRecords []FuelRecord `section:"FUEL RECORDS"`
2323
MaintenanceRecords []MaintenanceRecord `section:"MAINTENANCE RECORDS"`
24-
RoadTrips []RoadTripRecord `section:"ROAD TRIPS"`
24+
Trips []TripRecord `section:"ROAD TRIPS"`
2525
Tires []TireRecord `section:"TIRE LOG"`
2626
Valuations []ValuationRecord `section:"VALUATIONS"`
2727
Raw []byte
@@ -58,37 +58,43 @@ func (v *Vehicle) LoadFile(filename string) error {
5858
v.Raw = buf
5959
}
6060

61-
if err := v.Parse("FUEL RECORDS", &v.FuelRecords); err != nil {
62-
return fmt.Errorf("FuelRecords: %w", err)
61+
err = v.Parse("FUEL RECORDS", &v.FuelRecords)
62+
if err != nil {
63+
return fmt.Errorf("unable to parse FuelRecords: %w", err)
6364
}
6465

65-
if err := v.Parse("MAINTENANCE RECORDS", &v.MaintenanceRecords); err != nil {
66+
err = v.Parse("MAINTENANCE RECORDS", &v.MaintenanceRecords)
67+
if err != nil {
6668
return fmt.Errorf("MaintenanceRecords: %w", err)
6769
}
6870

69-
if err := v.Parse("ROAD TRIPS", &v.RoadTrips); err != nil {
70-
return fmt.Errorf("RoadTrips: %w", err)
71+
err = v.Parse("ROAD TRIPS", &v.Trips)
72+
if err != nil {
73+
return fmt.Errorf("unable to parse Trips: %w", err)
7174
}
7275

73-
if err := v.Parse("VEHICLE", &v.Vehicles); err != nil {
74-
return fmt.Errorf("Vehicle: %w", err)
76+
err = v.Parse("VEHICLE", &v.Vehicles)
77+
if err != nil {
78+
return fmt.Errorf("unable to parse Vehicle: %w", err)
7579
}
7680

77-
if err := v.Parse("TIRE LOG", &v.Tires); err != nil {
78-
return fmt.Errorf("TireLogs: %w", err)
81+
err = v.Parse("TIRE LOG", &v.Tires)
82+
if err != nil {
83+
return fmt.Errorf("unable to parse TireLogs: %w", err)
7984
}
8085

81-
if err := v.Parse("VALUATIONS", &v.Valuations); err != nil {
82-
return fmt.Errorf("Valuations: %w", err)
86+
err = v.Parse("VALUATIONS", &v.Valuations)
87+
if err != nil {
88+
return fmt.Errorf("unable to parse Valuations: %w", err)
8389
}
8490

85-
slog.Info("Loaded Road Trip CSV",
91+
slog.Debug("Loaded Road Trip CSV",
8692
"filename", v.Filename,
8793
"bytes", len(buf),
8894
"vehicleRecords", len(v.Vehicles),
8995
"fuelRecords", len(v.FuelRecords),
9096
"mainteanceRecords", len(v.MaintenanceRecords),
91-
"roadTrips", len(v.RoadTrips),
97+
"Trips", len(v.Trips),
9298
"tireLogs", len(v.Tires),
9399
"valuations", len(v.Valuations),
94100
)
@@ -98,15 +104,15 @@ func (v *Vehicle) LoadFile(filename string) error {
98104

99105
// Section returns a byte slice containing the raw contents of the specified section
100106
// from the corresponding [CSV] object.
101-
func (v *Vehicle) Section(sectionHeader string) (outbuf []byte) {
107+
func (v *Vehicle) Section(sectionHeader string) []byte {
102108
slog.Debug("Fetching Section from Raw",
103109
"sectionHeader", sectionHeader,
104110
)
105111

106112
sectionStart := make(map[string]int)
107113

108-
for index, element := range Sections {
109-
i := bytes.Index(v.Raw, []byte(Sections[index]))
114+
for index, element := range SectionHeaders {
115+
i := bytes.Index(v.Raw, []byte(SectionHeaders[index]))
110116
sectionStart[element] = i
111117

112118
slog.Debug("Section Start detected",
@@ -127,7 +133,7 @@ func (v *Vehicle) Section(sectionHeader string) (outbuf []byte) {
127133
// Don't include the section header line in the outbuf
128134
startPosition = startPosition + len(sectionHeader) + 1
129135

130-
outbuf = v.Raw[startPosition:endPosition]
136+
outbuf := v.Raw[startPosition:endPosition]
131137

132138
slog.Debug("Section Range calculated",
133139
"sectionHeader", sectionHeader,
@@ -136,7 +142,7 @@ func (v *Vehicle) Section(sectionHeader string) (outbuf []byte) {
136142
"sectionBytes", len(outbuf),
137143
)
138144

139-
return
145+
return outbuf
140146
}
141147

142148
// Parse unmarshalls the raw byte slice of the specified section from the underlying [CSV]
@@ -150,8 +156,8 @@ func (v *Vehicle) Parse(sectionHeader string, target interface{}) error {
150156
}
151157

152158
// ParseDate parses a Road Trip styled date string and turns it into a proper
153-
// Go [time.Time] value
154-
func ParseDate(dateString string) (t time.Time) {
159+
// Go [time.Time] value.
160+
func ParseDate(dateString string) time.Time {
155161
t, err := time.Parse("2006-1-2 15:04", dateString)
156162
if err != nil {
157163
t, err = time.Parse("2006-1-2", dateString)

roadtrip/roadtrip_test.go

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
package roadtrip
1+
package roadtrip_test

roadtrip/sections.go

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,18 @@ import (
88
)
99

1010
const (
11-
// Road Trip data file version number supported by this package
11+
// Road Trip data file version number supported by this package.
1212
SupportedVersion int64 = 1500
1313
)
1414

1515
// Each Road Trip "CSV" file is actually multiple, independent blocks of CSV
1616
// data delimited by two newlines and a section header string in all capital
1717
// letters.
1818
//
19-
// Sections contains a slice of strings corresponding to each of the section
20-
// headers found in the Road Trip data file. Currently this package only
21-
// supports Language "en" (see known issues in the README.md file)
22-
var Sections = []string{
19+
// SectionHeaders contains a slice of strings corresponding to each of the
20+
// section headers found in the Road Trip data file. Currently this package
21+
// only supports Language "en" (see known issues in the README.md file).
22+
var SectionHeaders = []string{
2323
"FUEL RECORDS",
2424
"MAINTENANCE RECORDS",
2525
"ROAD TRIPS",
@@ -66,7 +66,7 @@ type FuelRecord struct {
6666

6767
// LogValue is the handler for [log.slog] to emit structured output for a
6868
// [FuelRecord] object when logging.
69-
func (f FuelRecord) LogValue() slog.Value {
69+
func (f *FuelRecord) LogValue() slog.Value {
7070
return slog.GroupValue(
7171
slog.Float64("odometer", f.Odometer),
7272
slog.String("date", f.Date),
@@ -112,14 +112,14 @@ type MaintenanceRecord struct {
112112
NotificationDistance string `csv:"Notification Distance"`
113113
}
114114

115-
// A RoadTripRecord is a single CSV row from the Road Trip data file and
115+
// A TripRecord is a single CSV row from the Road Trip data file and
116116
// represents a road trip activity with all of its associated attributes. It is
117117
// date and odometer range bound with a start and end value for each of those
118118
// fields corresponding to the vehicle's service dates and odometer readings.
119119
//
120-
// A file will contain zero or more RoadTripRecord rows in the ROAD TRIPS section
120+
// A file will contain zero or more TripRecord rows in the ROAD TRIPS section
121121
// of the file.
122-
type RoadTripRecord struct {
122+
type TripRecord struct {
123123
Name string `csv:"Name"`
124124
StartDate string `csv:"Start Date"`
125125
StartOdometer float64 `csv:"Start Odometer (mi.),omitempty"`

0 commit comments

Comments
 (0)