Home

mint @main - refs - log -
-
https://git.jolheiser.com/mint.git
Budget
tree log patch
structure Signed-off-by: jolheiser <git@jolheiser.com>
Signature
-----BEGIN SSH SIGNATURE----- U1NIU0lHAAAAAQAAADMAAAALc3NoLWVkMjU1MTkAAAAgBTEvCQk6VqUAdN2RuH6bj1dNkY oOpbPWj+jw4ua1B1cAAAADZ2l0AAAAAAAAAAZzaGE1MTIAAABTAAAAC3NzaC1lZDI1NTE5 AAAAQHroA06J87lAQKcai1YBCTqC7SrMs3P3iUDVxAJL612KDN7K0apd94Ljlp5dazvuKg lggGjaYa/I8WvnaGy7jw8= -----END SSH SIGNATURE-----
jolheiser <git@jolheiser.com>
3 months ago
12 changed files, 358 additions(+), 3 deletions(-)
database/db.godatabase/models.godatabase/sqlc/migrations/001_schema.down.sqldatabase/sqlc/migrations/001_schema.up.sqldatabase/sqlc/migrations/migrations.godatabase/sqlc/queries/transactions.sqldatabase/sqlc/sqlc.godatabase/sqlc/sqlc.yamldatabase/transactions.sql.gogo.modgo.summain.go
I database/db.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
diff --git a/database/db.go b/database/db.go
new file mode 100644
index 0000000000000000000000000000000000000000..ef3e100691adf230cc629c22673f07e407872bc9
--- /dev/null
+++ b/database/db.go
@@ -0,0 +1,31 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+//   sqlc v1.29.0
+
+package database
+
+import (
+	"context"
+	"database/sql"
+)
+
+type DBTX interface {
+	ExecContext(context.Context, string, ...interface{}) (sql.Result, error)
+	PrepareContext(context.Context, string) (*sql.Stmt, error)
+	QueryContext(context.Context, string, ...interface{}) (*sql.Rows, error)
+	QueryRowContext(context.Context, string, ...interface{}) *sql.Row
+}
+
+func New(db DBTX) *Queries {
+	return &Queries{db: db}
+}
+
+type Queries struct {
+	db DBTX
+}
+
+func (q *Queries) WithTx(tx *sql.Tx) *Queries {
+	return &Queries{
+		db: tx,
+	}
+}
I database/models.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
diff --git a/database/models.go b/database/models.go
new file mode 100644
index 0000000000000000000000000000000000000000..2d7fc91cf703bf14521cf673885bd117ea53e670
--- /dev/null
+++ b/database/models.go
@@ -0,0 +1,17 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+//   sqlc v1.29.0
+
+package database
+
+import (
+	"time"
+)
+
+type Transaction struct {
+	ID         int64
+	Title      string
+	Amount     int64
+	Date       time.Time
+	Recurrance string
+}
I database/sqlc/migrations/001_schema.down.sql
1
2
3
4
5
6
7
diff --git a/database/sqlc/migrations/001_schema.down.sql b/database/sqlc/migrations/001_schema.down.sql
new file mode 100644
index 0000000000000000000000000000000000000000..91345dc7757af4cbbfa26447db2994b2ced84934
--- /dev/null
+++ b/database/sqlc/migrations/001_schema.down.sql
@@ -0,0 +1 @@
+DROP TABLE transactions;
I database/sqlc/migrations/001_schema.up.sql
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
diff --git a/database/sqlc/migrations/001_schema.up.sql b/database/sqlc/migrations/001_schema.up.sql
new file mode 100644
index 0000000000000000000000000000000000000000..9adffc94e5619c9e81bbc1b0ba56d014e58774c5
--- /dev/null
+++ b/database/sqlc/migrations/001_schema.up.sql
@@ -0,0 +1,7 @@
+CREATE TABLE transactions (
+    id         INTEGER PRIMARY KEY,
+    title      TEXT    NOT NULL,
+    amount     INTEGER NOT NULL,
+    date       DATE    NOT NULL,
+    recurrance TEXT    NOT NULL
+);
I database/sqlc/migrations/migrations.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
diff --git a/database/sqlc/migrations/migrations.go b/database/sqlc/migrations/migrations.go
new file mode 100644
index 0000000000000000000000000000000000000000..2e88b4f2b7dcd17cff0e675ba2c077d1f0a83375
--- /dev/null
+++ b/database/sqlc/migrations/migrations.go
@@ -0,0 +1,25 @@
+package migrations
+
+import (
+	"database/sql"
+
+	"go.jolheiser.com/mint/database/sqlc"
+
+	"github.com/golang-migrate/migrate/v4"
+	"github.com/golang-migrate/migrate/v4/database/sqlite"
+	_ "modernc.org/sqlite"
+)
+
+func New(db *sql.DB) (*migrate.Migrate, error) {
+	migrations, err := sqlc.Migrations()
+	if err != nil {
+		return nil, err
+	}
+
+	instance, err := sqlite.WithInstance(db, &sqlite.Config{})
+	if err != nil {
+		return nil, err
+	}
+
+	return migrate.NewWithInstance("sqlite", migrations, "mint", instance)
+}
I database/sqlc/queries/transactions.sql
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
diff --git a/database/sqlc/queries/transactions.sql b/database/sqlc/queries/transactions.sql
new file mode 100644
index 0000000000000000000000000000000000000000..7f96a96debdb5d2b23ebf9d9b958668087119c10
--- /dev/null
+++ b/database/sqlc/queries/transactions.sql
@@ -0,0 +1,25 @@
+-- name: GetTransaction :one
+SELECT * from transactions
+WHERE id = ? LIMIT 1;
+
+-- name: ListTransactions :many
+SELECT * FROM transactions
+WHERE date > ? AND date < ?;
+
+-- name: CreateTransaction :one
+INSERT INTO transactions (
+    title, amount, date, recurrance
+) VALUES (
+    ?, ?, ?, ?
+)
+RETURNING *;
+
+-- name: UpdateTransaction :one
+UPDATE transactions
+SET title = ?, amount = ?, date = ?, recurrance = ?
+WHERE id = ?
+RETURNING *;
+
+-- name: DeleteTransaction :exec
+DELETE FROM transactions
+WHERE id = ?;
I database/sqlc/sqlc.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
diff --git a/database/sqlc/sqlc.go b/database/sqlc/sqlc.go
new file mode 100644
index 0000000000000000000000000000000000000000..453ecc0c0ae67684749184d979cc8b9b93aaf567
--- /dev/null
+++ b/database/sqlc/sqlc.go
@@ -0,0 +1,21 @@
+//go:generate sqlc generate
+package sqlc
+
+import (
+	"embed"
+
+	_ "github.com/golang-migrate/migrate/v4/database/sqlite"
+	"github.com/golang-migrate/migrate/v4/source"
+	"github.com/golang-migrate/migrate/v4/source/iofs"
+)
+
+//go:embed migrations/*.sql
+var migrations embed.FS
+
+func Migrations() (source.Driver, error) {
+	d, err := iofs.New(migrations, "migrations")
+	if err != nil {
+		return nil, err
+	}
+	return d, nil
+}
I database/sqlc/sqlc.yaml
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
diff --git a/database/sqlc/sqlc.yaml b/database/sqlc/sqlc.yaml
new file mode 100644
index 0000000000000000000000000000000000000000..5ea6599551a5eb4367596b2bad4abf318dbe5e6a
--- /dev/null
+++ b/database/sqlc/sqlc.yaml
@@ -0,0 +1,9 @@
+version: "2"
+sql:
+  - engine: "sqlite"
+    schema: "migrations"
+    queries: "queries"
+    gen:
+      go:
+        package: "database"
+        out: "../"
I database/transactions.sql.go
  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
diff --git a/database/transactions.sql.go b/database/transactions.sql.go
new file mode 100644
index 0000000000000000000000000000000000000000..3db2b494275d52bc29f333ac66ab726c11b2d175
--- /dev/null
+++ b/database/transactions.sql.go
@@ -0,0 +1,146 @@
+// Code generated by sqlc. DO NOT EDIT.
+// versions:
+//   sqlc v1.29.0
+// source: transactions.sql
+
+package database
+
+import (
+	"context"
+	"time"
+)
+
+const createTransaction = `-- name: CreateTransaction :one
+INSERT INTO transactions (
+    title, amount, date, recurrance
+) VALUES (
+    ?, ?, ?, ?
+)
+RETURNING id, title, amount, date, recurrance
+`
+
+type CreateTransactionParams struct {
+	Title      string
+	Amount     int64
+	Date       time.Time
+	Recurrance string
+}
+
+func (q *Queries) CreateTransaction(ctx context.Context, arg CreateTransactionParams) (Transaction, error) {
+	row := q.db.QueryRowContext(ctx, createTransaction,
+		arg.Title,
+		arg.Amount,
+		arg.Date,
+		arg.Recurrance,
+	)
+	var i Transaction
+	err := row.Scan(
+		&i.ID,
+		&i.Title,
+		&i.Amount,
+		&i.Date,
+		&i.Recurrance,
+	)
+	return i, err
+}
+
+const deleteTransaction = `-- name: DeleteTransaction :exec
+DELETE FROM transactions
+WHERE id = ?
+`
+
+func (q *Queries) DeleteTransaction(ctx context.Context, id int64) error {
+	_, err := q.db.ExecContext(ctx, deleteTransaction, id)
+	return err
+}
+
+const getTransaction = `-- name: GetTransaction :one
+SELECT id, title, amount, date, recurrance from transactions
+WHERE id = ? LIMIT 1
+`
+
+func (q *Queries) GetTransaction(ctx context.Context, id int64) (Transaction, error) {
+	row := q.db.QueryRowContext(ctx, getTransaction, id)
+	var i Transaction
+	err := row.Scan(
+		&i.ID,
+		&i.Title,
+		&i.Amount,
+		&i.Date,
+		&i.Recurrance,
+	)
+	return i, err
+}
+
+const listTransactions = `-- name: ListTransactions :many
+SELECT id, title, amount, date, recurrance FROM transactions
+WHERE date > ? AND date < ?
+`
+
+type ListTransactionsParams struct {
+	Date   time.Time
+	Date_2 time.Time
+}
+
+func (q *Queries) ListTransactions(ctx context.Context, arg ListTransactionsParams) ([]Transaction, error) {
+	rows, err := q.db.QueryContext(ctx, listTransactions, arg.Date, arg.Date_2)
+	if err != nil {
+		return nil, err
+	}
+	defer rows.Close()
+	var items []Transaction
+	for rows.Next() {
+		var i Transaction
+		if err := rows.Scan(
+			&i.ID,
+			&i.Title,
+			&i.Amount,
+			&i.Date,
+			&i.Recurrance,
+		); err != nil {
+			return nil, err
+		}
+		items = append(items, i)
+	}
+	if err := rows.Close(); err != nil {
+		return nil, err
+	}
+	if err := rows.Err(); err != nil {
+		return nil, err
+	}
+	return items, nil
+}
+
+const updateTransaction = `-- name: UpdateTransaction :one
+UPDATE transactions
+SET title = ?, amount = ?, date = ?, recurrance = ?
+WHERE id = ?
+RETURNING id, title, amount, date, recurrance
+`
+
+type UpdateTransactionParams struct {
+	Title      string
+	Amount     int64
+	Date       time.Time
+	Recurrance string
+	ID         int64
+}
+
+func (q *Queries) UpdateTransaction(ctx context.Context, arg UpdateTransactionParams) (Transaction, error) {
+	row := q.db.QueryRowContext(ctx, updateTransaction,
+		arg.Title,
+		arg.Amount,
+		arg.Date,
+		arg.Recurrance,
+		arg.ID,
+	)
+	var i Transaction
+	err := row.Scan(
+		&i.ID,
+		&i.Title,
+		&i.Amount,
+		&i.Date,
+		&i.Recurrance,
+	)
+	return i, err
+}
M go.mod -> go.mod
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
diff --git a/go.mod b/go.mod
index 743bf91a918a7bd3a2d0194fbc277183f92f9672..ee70a6a9e1db226b35655ba44ab1d0bd269505fe 100644
--- a/go.mod
+++ b/go.mod
@@ -23,8 +23,12 @@ 	github.com/fatih/color v1.16.0 // indirect
 	github.com/fatih/structtag v1.2.0 // indirect
 	github.com/fsnotify/fsnotify v1.7.0 // indirect
 	github.com/go-sql-driver/mysql v1.9.2 // indirect
+	github.com/golang-migrate/migrate/v4 v4.18.3 // indirect
 	github.com/google/cel-go v0.24.1 // indirect
+	github.com/google/go-jsonnet v0.20.0 // indirect
 	github.com/google/uuid v1.6.0 // indirect
+	github.com/hashicorp/errwrap v1.1.0 // indirect
+	github.com/hashicorp/go-multierror v1.1.1 // indirect
 	github.com/inconshreveable/mousetrap v1.1.0 // indirect
 	github.com/jackc/pgpassfile v1.0.0 // indirect
 	github.com/jackc/pgservicefile v0.0.0-20240606120523-5a60cdf6a761 // indirect
@@ -35,6 +39,7 @@ 	github.com/mattn/go-colorable v0.1.13 // indirect
 	github.com/mattn/go-isatty v0.0.20 // indirect
 	github.com/natefinch/atomic v1.0.1 // indirect
 	github.com/ncruces/go-strftime v0.1.9 // indirect
+	github.com/peterbourgon/ff/v3 v3.4.0 // indirect
 	github.com/pganalyze/pg_query_go/v6 v6.1.0 // indirect
 	github.com/pingcap/errors v0.11.5-0.20240311024730-e056997136bb // indirect
 	github.com/pingcap/failpoint v0.0.0-20240528011301-b51a646c7c86 // indirect
@@ -49,6 +54,7 @@ 	github.com/stoewer/go-strcase v1.2.0 // indirect
 	github.com/tetratelabs/wazero v1.9.0 // indirect
 	github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 // indirect
 	github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 // indirect
+	go.jolheiser.com/ffjsonnet v0.0.0-20240816163546-0d0f7d30cc19 // indirect
 	go.uber.org/atomic v1.11.0 // indirect
 	go.uber.org/multierr v1.11.0 // indirect
 	go.uber.org/zap v1.27.0 // indirect
@@ -65,9 +71,11 @@ 	google.golang.org/genproto/googleapis/rpc v0.0.0-20250115164207-1a7da9e5054f // indirect
 	google.golang.org/grpc v1.71.1 // indirect
 	google.golang.org/protobuf v1.36.6 // indirect
 	gopkg.in/natefinch/lumberjack.v2 v2.2.1 // indirect
+	gopkg.in/yaml.v2 v2.4.0 // indirect
 	gopkg.in/yaml.v3 v3.0.1 // indirect
 	modernc.org/libc v1.62.1 // indirect
 	modernc.org/mathutil v1.7.1 // indirect
 	modernc.org/memory v1.9.1 // indirect
 	modernc.org/sqlite v1.37.0 // indirect
+	sigs.k8s.io/yaml v1.1.0 // indirect
 )
M go.sum -> go.sum
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
diff --git a/go.sum b/go.sum
index 672593b64ade08a81ed63ea114d94b5a21846ef8..c95c25ea15ef51c92ebec30348a429976bbcc556 100644
--- a/go.sum
+++ b/go.sum
@@ -36,6 +36,8 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
 github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
 github.com/go-sql-driver/mysql v1.9.2 h1:4cNKDYQ1I84SXslGddlsrMhc8k4LeDVj6Ad6WRjiHuU=
 github.com/go-sql-driver/mysql v1.9.2/go.mod h1:qn46aNg1333BRMNU69Lq93t8du/dwxI64Gl8i5p1WMU=
+github.com/golang-migrate/migrate/v4 v4.18.3 h1:EYGkoOsvgHHfm5U/naS1RP/6PL/Xv3S4B/swMiAmDLs=
+github.com/golang-migrate/migrate/v4 v4.18.3/go.mod h1:99BKpIi6ruaaXRM1A77eqZ+FWPQ3cfRa+ZVy5bmWMaY=
 github.com/golang/protobuf v1.5.0/go.mod h1:FsONVRAS9T7sI+LIUmWTfcYkHO4aIWwzhcaSAoJOfIk=
 github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek=
 github.com/golang/protobuf v1.5.4/go.mod h1:lnTiLA8Wa4RWRcIUkrtSVa5nRhsEGBg48fD6rSs7xps=
@@ -44,10 +46,17 @@ github.com/google/cel-go v0.24.1/go.mod h1:Hdf9TqOaTNSFQA1ybQaRqATVoK7m/zcf7IMhGXP5zI8=
 github.com/google/go-cmp v0.5.5/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
 github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
 github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
+github.com/google/go-jsonnet v0.20.0 h1:WG4TTSARuV7bSm4PMB4ohjxe33IHT5WVTrJSU33uT4g=
+github.com/google/go-jsonnet v0.20.0/go.mod h1:VbgWF9JX7ztlv770x/TolZNGGFfiHEVx9G6ca2eUmeA=
 github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e h1:ijClszYn+mADRFY17kjQEVQ1XRhq2/JR1M3sGqeJoxs=
 github.com/google/pprof v0.0.0-20250317173921-a4b03ec1a45e/go.mod h1:boTsfXsheKC2y+lKOCMpSfarhxDeIzfZG1jqGcPl3cA=
 github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
 github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
+github.com/hashicorp/errwrap v1.0.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/errwrap v1.1.0 h1:OxrOeh75EUXMY8TBjag2fzXGZ40LB6IKw45YeGUDY2I=
+github.com/hashicorp/errwrap v1.1.0/go.mod h1:YH+1FKiLXxHSkmPseP+kNlulaMuP3n2brvKWEqk/Jc4=
+github.com/hashicorp/go-multierror v1.1.1 h1:H5DkEtf6CXdFp0N0Em5UCwQpXMWke8IA0+lD48awMYo=
+github.com/hashicorp/go-multierror v1.1.1/go.mod h1:iw975J/qwKPdAO1clOe2L8331t/9/fmwbPZ6JB6eMoM=
 github.com/inconshreveable/mousetrap v1.1.0 h1:wN+x4NVGpMsO7ErUn/mUI3vEoE6Jt13X2s0bqwp9tc8=
 github.com/inconshreveable/mousetrap v1.1.0/go.mod h1:vpF70FUmC8bwa3OWnCshd2FqLfsEA9PFc4w1p2J65bw=
 github.com/jackc/pgpassfile v1.0.0 h1:/6Hmqy13Ss2zCq62VdNG8tM1wchn8zjSGOBJ6icpsIM=
@@ -76,6 +85,8 @@ github.com/natefinch/atomic v1.0.1 h1:ZPYKxkqQOx3KZ+RsbnP/YsgvxWQPGxjC0oBt2AhwV0A=
 github.com/natefinch/atomic v1.0.1/go.mod h1:N/D/ELrljoqDyT3rZrsUmtsuzvHkeB/wWjHV22AZRbM=
 github.com/ncruces/go-strftime v0.1.9 h1:bY0MQC28UADQmHmaF5dgpLmImcShSi2kHU9XLdhx/f4=
 github.com/ncruces/go-strftime v0.1.9/go.mod h1:Fwc5htZGVVkseilnfgOVb9mKy6w1naJmn9CehxcKcls=
+github.com/peterbourgon/ff/v3 v3.4.0 h1:QBvM/rizZM1cB0p0lGMdmR7HxZeI/ZrBWB4DqLkMUBc=
+github.com/peterbourgon/ff/v3 v3.4.0/go.mod h1:zjJVUhx+twciwfDl0zBcFzl4dW8axCRyXE/eKY9RztQ=
 github.com/pganalyze/pg_query_go/v6 v6.1.0 h1:jG5ZLhcVgL1FAw4C/0VNQaVmX1SUJx71wBGdtTtBvls=
 github.com/pganalyze/pg_query_go/v6 v6.1.0/go.mod h1:nvTHIuoud6e1SfrUaFwHqT0i4b5Nr+1rPWVds3B5+50=
 github.com/pingcap/errors v0.11.0/go.mod h1:Oi8TUi2kEtXXLMJk9l1cGmz20kV3TaQ0usTwv5KuLY8=
@@ -118,6 +129,8 @@ github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07 h1:mJdDDPblDfPe7z7go8Dvv1AJQDI3eQ/5xith3q2mFlo=
 github.com/wasilibs/go-pgquery v0.0.0-20250409022910-10ac41983c07/go.mod h1:Ak17IJ037caFp4jpCw/iQQ7/W74Sqpb1YuKJU6HTKfM=
 github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52 h1:OvLBa8SqJnZ6P+mjlzc2K7PM22rRUPE1x32G9DTPrC4=
 github.com/wasilibs/wazero-helpers v0.0.0-20240620070341-3dff1577cd52/go.mod h1:jMeV4Vpbi8osrE/pKUxRZkVaA0EX7NZN0A9/oRzgpgY=
+go.jolheiser.com/ffjsonnet v0.0.0-20240816163546-0d0f7d30cc19 h1:QRSdc/EmohEl+TkCU/l2frlwSIVR72HOZ1vHHhDwSwA=
+go.jolheiser.com/ffjsonnet v0.0.0-20240816163546-0d0f7d30cc19/go.mod h1:uW2oeieTw8qhLnwhImqMyGFFMW60mKgIoCp2JxtUHOQ=
 go.opentelemetry.io/auto/sdk v1.1.0 h1:cH53jehLUN6UFLY71z+NDOiNJqDdPRaXzTel0sJySYA=
 go.opentelemetry.io/auto/sdk v1.1.0/go.mod h1:3wSPjt5PWp2RhlCcmmOial7AvC4DQqZb7a7wCow3W8A=
 go.opentelemetry.io/otel v1.34.0 h1:zRLXxLCgL1WyKsPVrgbSdMN4c0FMkDAskSTQP+0hdUY=
@@ -194,6 +207,8 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1 h1:bBRl1b0OH9s/DuPhuXpNl+VtCaJXFZ5/uEFST95x9zc=
 gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYskCTPBJVb9jqSc=
 gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
 gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
+gopkg.in/yaml.v2 v2.4.0 h1:D8xgwECY7CYvx+Y2n4sBz93Jn9JRvxdiyyo8CTfuKaY=
+gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ=
 gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.0-20210107192922-496545a6307b/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
 gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
@@ -222,3 +237,5 @@ modernc.org/strutil v1.2.1 h1:UneZBkQA+DX2Rp35KcM69cSsNES9ly8mQWD71HKlOA0=
 modernc.org/strutil v1.2.1/go.mod h1:EHkiggD70koQxjVdSBM3JKM7k6L0FbGE5eymy9i3B9A=
 modernc.org/token v1.1.0 h1:Xl7Ap9dKaEs5kLoOQeQmPWevfnk/DM5qcLcYlA8ys6Y=
 modernc.org/token v1.1.0/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM=
+sigs.k8s.io/yaml v1.1.0 h1:4A07+ZFc2wgJwo8YNlQpr1rVlgUDlxXHhPJciaPY5gs=
+sigs.k8s.io/yaml v1.1.0/go.mod h1:UJmg0vDUVViEyp3mgSv9WPwZCDxu4rQW1olrI1uml+o=
M main.go -> main.go
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
diff --git a/main.go b/main.go
index 63c6df1a1e79a374424bb28b0ffa698df17ce77b..69842ae02be9b4c9501bda13cf083968d9172f70 100644
--- a/main.go
+++ b/main.go
@@ -11,7 +11,11 @@ 	"net/http"
 	"os"
 	"os/signal"
 	"strconv"
+	"strings"
 	"time"
+
+	"github.com/peterbourgon/ff/v3"
+	"go.jolheiser.com/ffjsonnet"
 )
 
 var (
@@ -30,14 +34,58 @@ 	Title         string         `json:"title"`
 	ExtendedProps map[string]any `json:"extendedProps"`
 }
 
+type Args struct {
+	Port     int
+	Database string
+	JSON     bool
+	LogLevel slog.Level
+}
+
 func maine() error {
+	args := Args{
+		LogLevel: slog.LevelInfo,
+	}
 	fs := flag.NewFlagSet("mint", flag.ExitOnError)
-	portFlag := fs.Int("port", 8080, "Port to run on")
-	fs.IntVar(portFlag, "p", *portFlag, "--port")
+	fs.IntVar(&args.Port, "port", 8080, "Port to run on")
+	fs.IntVar(&args.Port, "p", args.Port, "--port")
+	fs.StringVar(&args.Database, "database", "mint.sqlite3", "Path to database file")
+	fs.StringVar(&args.Database, "d", args.Database, "--database")
+	fs.BoolVar(&args.JSON, "json", false, "JSON logging")
+	fs.BoolVar(&args.JSON, "j", args.JSON, "--json")
+	logFn := func(s string) error {
+		switch strings.ToLower(s) {
+		case "debug":
+			args.LogLevel = slog.LevelDebug
+		case "info":
+			args.LogLevel = slog.LevelInfo
+		case "warn", "warning":
+			args.LogLevel = slog.LevelWarn
+		case "error":
+			args.LogLevel = slog.LevelError
+		default:
+			return fmt.Errorf("unknown log level %q: valid settings are debug, info, warn, or error")
+		}
+		return nil
+	}
+	fs.Func("log-level", "Logging level [debug, info, warn, error]", logFn)
+	fs.Func("l", "--log-level", logFn)
+	fs.String("config", "mint.jsonnet", "Config file")
 
-	if err := fs.Parse(os.Args[1:]); err != nil {
+	if err := ff.Parse(fs, os.Args[1:],
+		ff.WithConfigFileFlag("config"),
+		ff.WithAllowMissingConfigFile(true),
+		ff.WithConfigFileParser(ffjsonnet.Parser),
+		ff.WithEnvVarPrefix("MINT"),
+	); err != nil {
 		return fmt.Errorf("could not parse CLI arguments: %w", err)
 	}
+
+	logger := slog.New(slog.NewTextHandler(os.Stderr, nil))
+	if args.JSON {
+		logger = slog.New(slog.NewJSONHandler(os.Stderr, nil))
+	}
+	slog.SetDefault(logger)
+	slog.SetLogLoggerLevel(args.LogLevel)
 
 	mux := http.NewServeMux()
 	mux.HandleFunc("/", func(w http.ResponseWriter, r *http.Request) {