From 437127cf63f8664e22fd758dff51b28622e6dc6e Mon Sep 17 00:00:00 2001 From: "niklastasler@gmail.com" Date: Mon, 25 Dec 2023 12:03:28 +0100 Subject: [PATCH] feat: use sqlite as data storage --- .gitignore | 5 +- .vscode/settings.json | 11 + Makefile | 8 +- go.mod | 22 +- go.sum | 245 ++++++++++++++++ internal/api/handler.go | 101 +++++-- internal/skat/gen/model/game.go | 19 ++ internal/skat/gen/model/game_player.go | 14 + internal/skat/gen/model/player.go | 13 + internal/skat/gen/model/round.go | 22 ++ internal/skat/gen/model/round_opponent.go | 13 + internal/skat/gen/table/game.go | 84 ++++++ internal/skat/gen/table/game_player.go | 81 ++++++ internal/skat/gen/table/player.go | 78 +++++ internal/skat/gen/table/round.go | 93 ++++++ internal/skat/gen/table/round_opponent.go | 78 +++++ internal/skat/gen/table/table_use_schema.go | 18 ++ internal/skat/service.go | 274 +++++++++++++++--- internal/skat/skat.go | 40 +-- main.go | 61 +++- templates/GameDetails.templ | 2 +- templates/GameDetails_templ.go | 2 +- templates/GameOverview.templ | 2 +- templates/GameOverview_templ.go | 2 +- .../{gamelist.templ => game_card.templ} | 13 +- .../{gamelist_templ.go => game_card_templ.go} | 20 +- templates/components/rounds_list.templ | 16 +- templates/components/rounds_list_templ.go | 16 +- 28 files changed, 1227 insertions(+), 126 deletions(-) create mode 100644 .vscode/settings.json create mode 100644 internal/skat/gen/model/game.go create mode 100644 internal/skat/gen/model/game_player.go create mode 100644 internal/skat/gen/model/player.go create mode 100644 internal/skat/gen/model/round.go create mode 100644 internal/skat/gen/model/round_opponent.go create mode 100644 internal/skat/gen/table/game.go create mode 100644 internal/skat/gen/table/game_player.go create mode 100644 internal/skat/gen/table/player.go create mode 100644 internal/skat/gen/table/round.go create mode 100644 internal/skat/gen/table/round_opponent.go create mode 100644 internal/skat/gen/table/table_use_schema.go rename templates/components/{gamelist.templ => game_card.templ} (78%) rename templates/components/{gamelist_templ.go => game_card_templ.go} (86%) diff --git a/.gitignore b/.gitignore index 8bd3716..1cf3600 100644 --- a/.gitignore +++ b/.gitignore @@ -28,4 +28,7 @@ config.yaml # Compiled binaries bin/ -tmp/ \ No newline at end of file +tmp/ + +## DB files +*.sqlite \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 0000000..b769c96 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,11 @@ +{ + "sqltools.useNodeRuntime": true, + "sqltools.connections": [ + { + "previewLimit": 50, + "driver": "SQLite", + "database": "skat.sqlite", + "name": "skat" + } + ] +} diff --git a/Makefile b/Makefile index 8b207bc..675b688 100644 --- a/Makefile +++ b/Makefile @@ -5,13 +5,17 @@ TAGS := latest all: clean install gen tidy build -run: +run: gen go run main.go dev: air -build: +gen: + templ generate + jet -source=sqlite -dsn="./skat.sqlite" -schema=games -path=./internal/skat/gen + +build: gen go build -o bin/$(BINARY_NAME) main.go clean: diff --git a/go.mod b/go.mod index c9be30b..755e0fe 100644 --- a/go.mod +++ b/go.mod @@ -4,18 +4,38 @@ go 1.21.5 require ( github.com/a-h/templ v0.2.476 - github.com/google/uuid v1.5.0 + github.com/go-jet/jet/v2 v2.10.1 github.com/labstack/echo/v4 v4.11.4 + modernc.org/sqlite v1.28.0 ) require ( + github.com/davecgh/go-spew v1.1.1 // indirect + github.com/dustin/go-humanize v1.0.1 // indirect + github.com/google/uuid v1.5.0 // indirect + github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 // indirect github.com/labstack/gommon v0.4.2 // indirect github.com/mattn/go-colorable v0.1.13 // indirect github.com/mattn/go-isatty v0.0.20 // indirect + github.com/pmezard/go-difflib v1.0.0 // indirect + github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec // indirect + github.com/stretchr/testify v1.8.4 // indirect github.com/valyala/bytebufferpool v1.0.0 // indirect github.com/valyala/fasttemplate v1.2.2 // indirect golang.org/x/crypto v0.17.0 // indirect + golang.org/x/mod v0.8.0 // indirect golang.org/x/net v0.19.0 // indirect golang.org/x/sys v0.15.0 // indirect golang.org/x/text v0.14.0 // indirect + golang.org/x/tools v0.6.0 // indirect + gopkg.in/yaml.v3 v3.0.1 // indirect + lukechampine.com/uint128 v1.2.0 // indirect + modernc.org/cc/v3 v3.40.0 // indirect + modernc.org/ccgo/v3 v3.16.13 // indirect + modernc.org/libc v1.29.0 // indirect + modernc.org/mathutil v1.6.0 // indirect + modernc.org/memory v1.7.2 // indirect + modernc.org/opt v0.1.3 // indirect + modernc.org/strutil v1.1.3 // indirect + modernc.org/token v1.0.1 // indirect ) diff --git a/go.sum b/go.sum index d673da4..506927c 100644 --- a/go.sum +++ b/go.sum @@ -1,37 +1,282 @@ +github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= +github.com/Masterminds/semver/v3 v3.1.1/go.mod h1:VPu/7SZ7ePZ3QOrcuXROw5FAcLl4a0cBrbBpGY/8hQs= github.com/a-h/templ v0.2.476 h1:+H4hP4CwK4kfJwXsE6kHeFWMGtcVOVoOm/I64uzARBk= github.com/a-h/templ v0.2.476/go.mod h1:zQ95mSyadNTGHv6k5Fm+wQU8zkBMMbHCHg7eAvUZKNM= +github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI= +github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI= +github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU= +github.com/cockroachdb/apd v1.1.0/go.mod h1:8Sl8LxpKi29FqWXR16WEFZRNSz3SoPzUzeMeY4+DwBQ= +github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/coreos/go-systemd v0.0.0-20190719114852-fd7a80b32e1f/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= +github.com/creack/pty v1.1.7/go.mod h1:lj5s0c3V2DBrqTV7llrYr5NG6My20zk30Fl46Y7DoTY= +github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= +github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= +github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/felixge/fgprof v0.9.3/go.mod h1:RdbpDgzqYVh/T9fPELJyV7EYJuHB55UTEULNun8eiPw= +github.com/friendsofgo/errors v0.9.2/go.mod h1:yCvFW5AkDIL9qn7suHVLiI/gH228n7PC4Pn44IGoTOI= +github.com/go-jet/jet/v2 v2.10.1 h1:mOKE5S+mt5bM/xNiuD7Dcz+FdqM83zg1FpOzfTJGJNw= +github.com/go-jet/jet/v2 v2.10.1/go.mod h1:XF5x5l7W4g7S9Rok9aXfPARFUyurl61nc+UNhuxKlYA= +github.com/go-kit/log v0.1.0/go.mod h1:zbhenjAZHb184qTLMA9ZjW7ThYL0H2mk7Q6pNt4vbaY= +github.com/go-logfmt/logfmt v0.5.0/go.mod h1:wCYkCAKZfumFQihp8CzCvQ3paCTfi41vtzG1KdI/P7A= +github.com/go-sql-driver/mysql v1.7.0/go.mod h1:OXbVy3sEdcQ2Doequ6Z5BW6fXNQTmx+9S1MCJN5yJMI= +github.com/go-stack/stack v1.8.0/go.mod h1:v0f6uXyyMGvRgIKkXu+yp6POWl0qKG85gN/melR3HDY= +github.com/gofrs/uuid v3.2.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/gofrs/uuid v4.0.0+incompatible/go.mod h1:b2aQJv3Z4Fp6yNu3cdSllBxTCLRxnplIgP/c0N/04lM= +github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= github.com/google/go-cmp v0.6.0 h1:ofyhxvXcZhMsU5ulbFiLKl/XBFqE1GSq7atu8tAmTRI= github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY= +github.com/google/pprof v0.0.0-20211214055906-6f57359322fd/go.mod h1:KgnwoLYCZ8IQu3XUZ8Nc/bM9CCZFOyjUNOSygVozoDg= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26 h1:Xim43kblpZXfIBQsbuBVKCudVG457BR2GZFIz3uw3hQ= +github.com/google/pprof v0.0.0-20221118152302-e6195bd50e26/go.mod h1:dDKJzRmX4S37WGHujM7tX//fmj1uioxKzKxz3lo4HJo= +github.com/google/renameio v0.1.0/go.mod h1:KWCgfxg9yswjAJkECMjeO8J8rahYeXnNhOm40UhjYkI= +github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= github.com/google/uuid v1.5.0 h1:1p67kYwdtXjb0gL0BPiP1Av9wiZPo5A8z2cWkTZ+eyU= github.com/google/uuid v1.5.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= +github.com/ianlancetaylor/demangle v0.0.0-20210905161508-09a460cdf81d/go.mod h1:aYm2/VgdVmcIU8iMfdMvDMsRAQjcfZSKFby6HOFvi/w= +github.com/jackc/chunkreader v1.0.0/go.mod h1:RT6O25fNZIuasFJRyZ4R/Y2BbhasbmZXF9QQ7T3kePo= +github.com/jackc/chunkreader/v2 v2.0.0/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/chunkreader/v2 v2.0.1/go.mod h1:odVSm741yZoC3dpHEUXIqA9tQRhFrgOHwnPIn9lDKlk= +github.com/jackc/pgconn v0.0.0-20190420214824-7e0022ef6ba3/go.mod h1:jkELnwuX+w9qN5YIfX0fl88Ehu4XC3keFuOJJk9pcnA= +github.com/jackc/pgconn v0.0.0-20190824142844-760dd75542eb/go.mod h1:lLjNuW/+OfW9/pnVKPazfWOgNfH2aPem8YQ7ilXGvJE= +github.com/jackc/pgconn v0.0.0-20190831204454-2fabfa3c18b7/go.mod h1:ZJKsE/KZfsUgOEh9hBm+xYTstcNHg7UPMVJqRfQxq4s= +github.com/jackc/pgconn v1.8.0/go.mod h1:1C2Pb36bGIP9QHGBYCjnyhqu7Rv3sGshaQUvmfGIB/o= +github.com/jackc/pgconn v1.9.0/go.mod h1:YctiPyvzfU11JFxoXokUOOKQXQmDMoJL9vJzHH8/2JY= +github.com/jackc/pgconn v1.9.1-0.20210724152538-d89c8390a530/go.mod h1:4z2w8XhRbP1hYxkpTuBjTS3ne3J48K83+u0zoyvg2pI= +github.com/jackc/pgconn v1.14.0/go.mod h1:9mBNlny0UvkgJdCDvdVHYSjI+8tD2rnKK69Wz8ti++E= +github.com/jackc/pgio v1.0.0/go.mod h1:oP+2QK2wFfUWgr+gxjoBH9KGBb31Eio69xUb0w5bYf8= +github.com/jackc/pgmock v0.0.0-20190831213851-13a1b77aafa2/go.mod h1:fGZlG77KXmcq05nJLRkk0+p82V8B8Dw8KN2/V9c/OAE= +github.com/jackc/pgmock v0.0.0-20201204152224-4fe30f7445fd/go.mod h1:hrBW0Enj2AZTNpt/7Y5rr2xe/9Mn757Wtb2xeBzPv2c= +github.com/jackc/pgmock v0.0.0-20210724152146-4ad1a8207f65/go.mod h1:5R2h2EEX+qri8jOWMbJCtaPWkrrNc7OHwsp2TCqp7ak= +github.com/jackc/pgpassfile v1.0.0/go.mod h1:CEx0iS5ambNFdcRtxPj5JhEz+xB6uRky5eyVu/W2HEg= +github.com/jackc/pgproto3 v1.1.0/go.mod h1:eR5FA3leWg7p9aeAqi37XOTgTIbkABlvcPB3E5rlc78= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190420180111-c116219b62db/go.mod h1:bhq50y+xrl9n5mRYyCBFKkpRVTLYJVWeCc+mEAI3yXA= +github.com/jackc/pgproto3/v2 v2.0.0-alpha1.0.20190609003834-432c2951c711/go.mod h1:uH0AWtUmuShn0bcesswc4aBTWGvw0cAxIJp+6OB//Wg= +github.com/jackc/pgproto3/v2 v2.0.0-rc3/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.0-rc3.0.20190831210041-4c03ce451f29/go.mod h1:ryONWYqW6dqSg1Lw6vXNMXoBJhpzvWKnT95C46ckYeM= +github.com/jackc/pgproto3/v2 v2.0.6/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.1.1/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgproto3/v2 v2.3.2/go.mod h1:WfJCnwN3HIg9Ish/j3sgWXnAfK8A9Y0bwXYU5xKaEdA= +github.com/jackc/pgservicefile v0.0.0-20200714003250-2b9c44734f2b/go.mod h1:vsD4gTJCa9TptPL8sPkXrLZ+hDuNrZCnj29CQpr4X1E= +github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a/go.mod h1:5TJZWKEWniPve33vlWYSoGYefn3gLQRzjfDlhSJ9ZKM= +github.com/jackc/pgtype v0.0.0-20190421001408-4ed0de4755e0/go.mod h1:hdSHsc1V01CGwFsrv11mJRHWJ6aifDLfdV3aVjFF0zg= +github.com/jackc/pgtype v0.0.0-20190824184912-ab885b375b90/go.mod h1:KcahbBH1nCMSo2DXpzsoWOAfFkdEtEJpPbVLq8eE+mc= +github.com/jackc/pgtype v0.0.0-20190828014616-a8802b16cc59/go.mod h1:MWlu30kVJrUS8lot6TQqcg7mtthZ9T0EoIBFiJcmcyw= +github.com/jackc/pgtype v1.8.1-0.20210724151600-32e20a603178/go.mod h1:C516IlIV9NKqfsMCXTdChteoXmwgUceqaLfjg2e3NlM= +github.com/jackc/pgtype v1.14.0/go.mod h1:LUMuVrfsFfdKGLw+AFFVv6KtHOFMwRgDDzBt76IqCA4= +github.com/jackc/pgx/v4 v4.0.0-20190420224344-cc3461e65d96/go.mod h1:mdxmSJJuR08CZQyj1PVQBHy9XOp5p8/SHH6a0psbY9Y= +github.com/jackc/pgx/v4 v4.0.0-20190421002000-1b8f0016e912/go.mod h1:no/Y67Jkk/9WuGR0JG/JseM9irFbnEPbuWV2EELPNuM= +github.com/jackc/pgx/v4 v4.0.0-pre1.0.20190824185557-6972a5742186/go.mod h1:X+GQnOEnf1dqHGpw7JmHqHc1NxDoalibchSk9/RWuDc= +github.com/jackc/pgx/v4 v4.12.1-0.20210724153913-640aa07df17c/go.mod h1:1QD0+tgSXP7iUjYm9C1NxKhny7lq6ee99u/z+IHFcgs= +github.com/jackc/pgx/v4 v4.18.1/go.mod h1:FydWkUyadDmdNH/mHnGob881GawxeEm7TcMCzkb+qQE= +github.com/jackc/puddle v0.0.0-20190413234325-e4ced69a3a2b/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v0.0.0-20190608224051-11cab39313c9/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.1.3/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/jackc/puddle v1.3.0/go.mod h1:m4B5Dj62Y0fbyuIc15OsIqK0+JU8nkqQjsgx7dvjSWk= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs= +github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8= +github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/konsorten/go-windows-terminal-sequences v1.0.2/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= +github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI= +github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= +github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ= +github.com/kr/pty v1.1.8/go.mod h1:O1sed60cT9XZ5uDucP5qwvh+TE3NnUj51EiZO/lmSfw= +github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE= +github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI= github.com/labstack/echo/v4 v4.11.4 h1:vDZmA+qNeh1pd/cCkEicDMrjtrnMGQ1QFI9gWN1zGq8= github.com/labstack/echo/v4 v4.11.4/go.mod h1:noh7EvLwqDsmh/X/HWKPUl1AjzJrhyptRyEbQJfxen8= github.com/labstack/gommon v0.4.2 h1:F8qTUNXgG1+6WQmqoUWnz8WiEU60mXVVw0P4ht1WRA0= github.com/labstack/gommon v0.4.2/go.mod h1:QlUFxVM+SNXhDL/Z7YhocGIBYOiwB0mXm1+1bAPHPyU= +github.com/lib/pq v1.0.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.1.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.2.0/go.mod h1:5WUZQaWbwv1U+lTReE5YruASi9Al49XbQIvNi/34Woo= +github.com/lib/pq v1.10.2/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/lib/pq v1.10.8/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= +github.com/mattn/go-colorable v0.1.1/go.mod h1:FuOcm+DKB9mbwrcAfNl7/TZVBZ6rcnceauSikq3lYCQ= +github.com/mattn/go-colorable v0.1.6/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc= github.com/mattn/go-colorable v0.1.13 h1:fFA4WZxdEF4tXPZVKMLwD8oUnCTTo08duU7wxecdEvA= github.com/mattn/go-colorable v0.1.13/go.mod h1:7S9/ev0klgBDR4GtXTXX8a3vIGJpMovkB8vQcUbaXHg= +github.com/mattn/go-isatty v0.0.5/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.7/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s= +github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU= github.com/mattn/go-isatty v0.0.16/go.mod h1:kYGgaQfpe5nmfYZH+SKPsOc2e4SrIfOl2e/yFXSvRLM= github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY= github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y= +github.com/mattn/go-sqlite3 v1.14.16 h1:yOQRA0RpS5PFz/oikGwBEqvAWhWg5ufRz4ETLjwpU1Y= +github.com/mattn/go-sqlite3 v1.14.16/go.mod h1:2eHXhiwb8IkHr+BDWZGa96P6+rkvnG63S2DGjv9HUNg= +github.com/pkg/errors v0.8.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/pkg/profile v1.7.0/go.mod h1:8Uer0jas47ZQMJ7VD+OHknK4YDY07LPUC6dEvqDjvNo= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec h1:W09IVJc94icq4NjY3clb7Lk8O1qJ8BdBEF8z0ibU0rE= +github.com/remyoudompheng/bigfft v0.0.0-20230129092748-24d4a6f8daec/go.mod h1:qqbHyh8v60DhA7CoWK5oRCqLrMHRGoxYCSS9EjAz6Eo= +github.com/rogpeppe/go-internal v1.3.0/go.mod h1:M8bDsm7K2OlrFYOpmOWEs/qY81heoFRclV5y23lUDJ4= +github.com/rs/xid v1.2.1/go.mod h1:+uKXf+4Djp6Md1KODXJxgGQPKngRmWyn10oCKFzNHOQ= +github.com/rs/zerolog v1.13.0/go.mod h1:YbFCdg8HfsridGWAh22vktObvhZbQsZXe4/zB0OKkWU= +github.com/rs/zerolog v1.15.0/go.mod h1:xYTKnLHcpfU2225ny5qZjxnj9NvkumZYjJHlAThCjNc= +github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0= +github.com/shopspring/decimal v0.0.0-20180709203117-cd690d0c9e24/go.mod h1:M+9NzErvs504Cn4c5DxATwIqPbtswREoFCre64PpcG4= +github.com/shopspring/decimal v1.2.0/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/shopspring/decimal v1.3.1/go.mod h1:DKyhrW/HYNuLGql+MJL6WCR6knT2jwCFRcu2hWCYk4o= +github.com/sirupsen/logrus v1.4.1/go.mod h1:ni0Sbl8bgC9z8RoU9G6nDWqqs/fq4eDPysMBDgk/93Q= +github.com/sirupsen/logrus v1.4.2/go.mod h1:tLMulIdttU9McNUspp0xgXVQah82FyeX6MwdIuYE2rE= +github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME= +github.com/stretchr/objx v0.2.0/go.mod h1:qt09Ya8vawLte6SNmTgCsAVtYtaKzEcn8ATUoHMkEqE= +github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSSt89Yw= +github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= +github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs= +github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI= +github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4= +github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA= +github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.7.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= +github.com/stretchr/testify v1.8.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU= +github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= +github.com/stretchr/testify v1.8.2/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4= github.com/stretchr/testify v1.8.4 h1:CcVxjf3Q8PM0mHUKJCdn+eZZtm5yQwehR5yeSVQQcUk= github.com/stretchr/testify v1.8.4/go.mod h1:sz/lmYIOXD/1dqDmKjjqLyZ2RngseejIcXlSw2iwfAo= github.com/valyala/bytebufferpool v1.0.0 h1:GqA5TC/0021Y/b9FG4Oi9Mr3q7XYx6KllzawFIhcdPw= github.com/valyala/bytebufferpool v1.0.0/go.mod h1:6bBcMArwyJ5K/AmCkWv1jt77kVWyCJ6HpOuEn7z0Csc= github.com/valyala/fasttemplate v1.2.2 h1:lxLXG0uE3Qnshl9QyaK6XJxMXlQZELvChBOCmQD0Loo= github.com/valyala/fasttemplate v1.2.2/go.mod h1:KHLXt3tVN2HBp8eijSv/kGJopbvo7S+qRAEEKiv+SiQ= +github.com/volatiletech/inflect v0.0.1/go.mod h1:IBti31tG6phkHitLlr5j7shC5SOo//x0AjDzaJU1PLA= +github.com/volatiletech/null/v8 v8.1.2/go.mod h1:98DbwNoKEpRrYtGjWFctievIfm4n4MxG0A6EBUcoS5g= +github.com/volatiletech/randomize v0.0.1/go.mod h1:GN3U0QYqfZ9FOJ67bzax1cqZ5q2xuj2mXrXBjWaRTlY= +github.com/volatiletech/strmangle v0.0.1/go.mod h1:F6RA6IkB5vq0yTG4GQ0UsbbRcl3ni9P76i+JrTBKFFg= +github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY= +github.com/zenazn/goji v0.9.0/go.mod h1:7S9M489iMyHBNxwZnk9/EHS098H4/F6TATF2mIxtB1Q= +go.uber.org/atomic v1.3.2/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= +go.uber.org/atomic v1.5.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/atomic v1.6.0/go.mod h1:sABNBOSYdrvTF6hTgEIbc7YasKWGhgEQZyfxyTvoXHQ= +go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= +go.uber.org/multierr v1.3.0/go.mod h1:VgVr7evmIr6uPjLBxg28wmKNXyqE9akIJ5XnfpiKl+4= +go.uber.org/multierr v1.5.0/go.mod h1:FeouvMocqHpRaaGuG9EjoKcStLC43Zu/fmqdUMPcKYU= +go.uber.org/tools v0.0.0-20190618225709-2cfd321de3ee/go.mod h1:vJERXedbb3MVM5f9Ejo0C68/HhF8uaILCdgjnY+goOA= +go.uber.org/zap v1.9.1/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +go.uber.org/zap v1.13.0/go.mod h1:zwrFLgMcdUuIBviXEYEH1YKNaOBnKXsx2IPda5bBwHM= +golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= +golang.org/x/crypto v0.0.0-20190411191339-88737f569e3a/go.mod h1:WFFai1msRO1wXaEeE5yQxYXgSfI8pQAWXbQop6sCtWE= +golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= +golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20201203163018-be400aefbc4c/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I= +golang.org/x/crypto v0.0.0-20210616213533-5ff15b29337e/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210711020723-a769d52b0f97/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= +golang.org/x/crypto v0.6.0/go.mod h1:OFC/31mSvZgRz0V1QTNCzfAI1aIRzbiufJtkMIlEp58= golang.org/x/crypto v0.17.0 h1:r8bRNjWL3GshPW3gkd+RpvzWrZAwPS49OmTGZ/uhM4k= golang.org/x/crypto v0.17.0/go.mod h1:gCAAfMLgwOJRpTjQ2zCCt2OcSfYMTeZVSRtQlPC7Nq4= +golang.org/x/lint v0.0.0-20190930215403-16217165b5de/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= +golang.org/x/mod v0.0.0-20190513183733-4bf6d317e70e/go.mod h1:mXi4GBBbnImb6dmsKGUJ2LatrhH/nqhxcFungHvyanc= +golang.org/x/mod v0.1.1-0.20191105210325-c90efee705ee/go.mod h1:QqPTAvyqsEbceGzBzNggFXnrqF1CaUcvgkdR5Ot7KZg= +golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= +golang.org/x/mod v0.8.0 h1:LUYupSeNrTNCGzR/hVBk2NHZO4hXcVaW1k4Qx7rjPx8= +golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= +golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= +golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20190813141303-74dc4d7220e7/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s= +golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg= +golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= +golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.19.0 h1:zTwKpTd2XuCqf8huc7Fo2iSy+4RHPd10s4KzeTnVr1c= golang.org/x/net v0.19.0/go.mod h1:CfAk/cbD4CthTvqiEl8NpboMuiuOYsAr/7NOjZJtv1U= +golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sync v0.1.0 h1:wsuoTGHzEhffawBOhz5CYhcrV4IdKZbEyZjBMuTp12o= +golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= +golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20190403152447-81d4e9dc473e/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190422165155-953cdadca894/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs= +golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20211007075335-d3039528d8ac/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= +golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.15.0 h1:h48lPFYpsTvQJZF4EKyI4aLHaev3CxivZmv7yZig9pc= golang.org/x/sys v0.15.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= +golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw= +golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= +golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= +golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= +golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= +golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk= +golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.4/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= +golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= +golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.14.0 h1:ScX5w1eTa3QqT8oi6+ziP7dTV1S2+ALU0bI+0zXKWiQ= golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= +golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ= +golang.org/x/tools v0.0.0-20190311212946-11955173bddd/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs= +golang.org/x/tools v0.0.0-20190425163242-31fd60d6bfdc/go.mod h1:RgjU9mgBXZiqYHBnxXauZ1Gv1EHHAz9KjViQ78xBX0Q= +golang.org/x/tools v0.0.0-20190621195816-6e04913cbbac/go.mod h1:/rFqwRUd4F7ZHNgwSSTFct+R/Kf4OFW1sUzUTQQTgfc= +golang.org/x/tools v0.0.0-20190823170909-c4a336ef6a2f/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029041327-9cc4af7d6b2c/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191029190741-b9c20aec41a5/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= +golang.org/x/tools v0.0.0-20200103221440-774c71fcf114/go.mod h1:TB2adYChydJhpapKDTa4BR/hXlZSLoq2Wpct/0txZ28= +golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= +golang.org/x/tools v0.6.0 h1:BOw41kyTf3PuCW1pVQf8+Cyg8pMlkYB1oo9iJ6D/lKM= +golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= +golang.org/x/xerrors v0.0.0-20190410155217-1f06c39b4373/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190513163551-3ee3066db522/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= +gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY= +gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/errgo.v2 v2.1.0/go.mod h1:hNsd1EY+bozCKY1Ytp96fpM3vjJbqLJn88ws8XvfDNI= +gopkg.in/guregu/null.v4 v4.0.0/go.mod h1:YoQhUrADuG3i9WqesrCmpNRwm1ypAgSHYqoOcTu/JrI= +gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= +gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= +gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= +honnef.co/go/tools v0.0.1-2019.2.3/go.mod h1:a3bituU0lyd329TUQxRnasdCoJDkEUEAqEt0JzvZhAg= +lukechampine.com/uint128 v1.2.0 h1:mBi/5l91vocEN8otkC5bDLhi2KdCticRiwbdB0O+rjI= +lukechampine.com/uint128 v1.2.0/go.mod h1:c4eWIwlEGaxC/+H1VguhU4PHXNWDCDMUlWdIWl2j1gk= +modernc.org/cc/v3 v3.40.0 h1:P3g79IUS/93SYhtoeaHW+kRCIrYaxJ27MFPv+7kaTOw= +modernc.org/cc/v3 v3.40.0/go.mod h1:/bTg4dnWkSXowUO6ssQKnOV0yMVxDYNIsIrzqTFDGH0= +modernc.org/ccgo/v3 v3.16.13 h1:Mkgdzl46i5F/CNR/Kj80Ri59hC8TKAhZrYSaqvkwzUw= +modernc.org/ccgo/v3 v3.16.13/go.mod h1:2Quk+5YgpImhPjv2Qsob1DnZ/4som1lJTodubIcoUkY= +modernc.org/ccorpus v1.11.6 h1:J16RXiiqiCgua6+ZvQot4yUuUy8zxgqbqEEUuGPlISk= +modernc.org/ccorpus v1.11.6/go.mod h1:2gEUTrWqdpH2pXsmTM1ZkjeSrUWDpjMu2T6m29L/ErQ= +modernc.org/httpfs v1.0.6 h1:AAgIpFZRXuYnkjftxTAZwMIiwEqAfk8aVB2/oA6nAeM= +modernc.org/httpfs v1.0.6/go.mod h1:7dosgurJGp0sPaRanU53W4xZYKh14wfzX420oZADeHM= +modernc.org/libc v1.29.0 h1:tTFRFq69YKCF2QyGNuRUQxKBm1uZZLubf6Cjh/pVHXs= +modernc.org/libc v1.29.0/go.mod h1:DaG/4Q3LRRdqpiLyP0C2m1B8ZMGkQ+cCgOIjEtQlYhQ= +modernc.org/mathutil v1.6.0 h1:fRe9+AmYlaej+64JsEEhoWuAYBkOtQiMEU7n/XgfYi4= +modernc.org/mathutil v1.6.0/go.mod h1:Ui5Q9q1TR2gFm0AQRqQUaBWFLAhQpCwNcuhBOSedWPo= +modernc.org/memory v1.7.2 h1:Klh90S215mmH8c9gO98QxQFsY+W451E8AnzjoE2ee1E= +modernc.org/memory v1.7.2/go.mod h1:NO4NVCQy0N7ln+T9ngWqOQfi7ley4vpwvARR+Hjw95E= +modernc.org/opt v0.1.3 h1:3XOZf2yznlhC+ibLltsDGzABUGVx8J6pnFMS3E4dcq4= +modernc.org/opt v0.1.3/go.mod h1:WdSiB5evDcignE70guQKxYUl14mgWtbClRi5wmkkTX0= +modernc.org/sqlite v1.28.0 h1:Zx+LyDDmXczNnEQdvPuEfcFVA2ZPyaD7UCZDjef3BHQ= +modernc.org/sqlite v1.28.0/go.mod h1:Qxpazz0zH8Z1xCFyi5GSL3FzbtZ3fvbjmywNogldEW0= +modernc.org/strutil v1.1.3 h1:fNMm+oJklMGYfU9Ylcywl0CO5O6nTfaowNsh2wpPjzY= +modernc.org/strutil v1.1.3/go.mod h1:MEHNA7PdEnEwLvspRMtWTNnp2nnyvMfkimT1NKNAGbw= +modernc.org/tcl v1.15.2 h1:C4ybAYCGJw968e+Me18oW55kD/FexcHbqH2xak1ROSY= +modernc.org/tcl v1.15.2/go.mod h1:3+k/ZaEbKrC8ePv8zJWPtBSW0V7Gg9g8rkmhI1Kfs3c= +modernc.org/token v1.0.1 h1:A3qvTqOwexpfZZeyI0FeGPDlSWX5pjZu9hF4lU+EKWg= +modernc.org/token v1.0.1/go.mod h1:UGzOrNV1mAFSEB63lOFHIpNRUVMvYTc6yu1SMY/XTDM= +modernc.org/z v1.7.3 h1:zDJf6iHjrnB+WRD88stbXokugjyc0/pB91ri1gO6LZY= +modernc.org/z v1.7.3/go.mod h1:Ipv4tsdxZRbQyLq9Q1M6gdbkxYzdlrciF2Hi/lS7nWE= diff --git a/internal/api/handler.go b/internal/api/handler.go index 20801b3..881b14a 100644 --- a/internal/api/handler.go +++ b/internal/api/handler.go @@ -5,12 +5,14 @@ import ( "fmt" "net/http" "strconv" + "time" "github.com/a-h/templ" "github.com/labstack/echo/v4" + "github.com/labstack/gommon/log" "github.com/tarow/skat-counter/internal/skat" + "github.com/tarow/skat-counter/internal/skat/gen/model" template "github.com/tarow/skat-counter/templates" - "github.com/tarow/skat-counter/templates/components" ) type Handler struct { @@ -24,7 +26,12 @@ func NewHandler(service skat.Service) Handler { } func (h Handler) GetIndex(c echo.Context) error { - index := template.GameOverview(h.service.List()) + games, err := h.service.List() + if err != nil { + c.Logger().Error(err) + return err + } + index := template.GameOverview(games) return render(c, http.StatusOK, index) } @@ -33,13 +40,16 @@ func (h Handler) GetGameDetails(c echo.Context) error { parsedId, err := strconv.Atoi(gameId) if err != nil { + c.Logger().Error(err) return err } - game := h.service.Find(parsedId) + game, err := h.service.Find(int32(parsedId)) if err != nil { + c.Logger().Error(err) return err } + fmt.Printf("%+v", game) gameDetails := template.GameDetails(*game) @@ -47,50 +57,92 @@ func (h Handler) GetGameDetails(c echo.Context) error { } func (h Handler) CreateGame(c echo.Context) error { + createGameForm := struct { + Players []string `form:"player"` + Online bool `form:"online"` + Stake float32 `form:"stake"` + }{} + + err := c.Bind(&createGameForm) + if err != nil { + c.Logger().Error(err) + return err + } + + players := []model.Player{} + for _, p := range createGameForm.Players { + players = append(players, model.Player{Name: p}) + } g := skat.Game{} - c.Bind(&g) - fmt.Println("received game", g) - g = h.service.Create(g) - fmt.Println("created game", g) + g.Players = players + + g.Online = true + g.Stake = 1.5 + g.CreatedAt = time.Now() + + g, err = h.service.Create(g) + if err != nil { + return err + } + details := template.GameDetails(g) c.Response().Header().Set("Access-Control-Expose-Headers", "*") - //c.Response().Header().Set("Access-Control-Allow-Origin", "*") - //c.Response().Header().Set("Access-Control-Allow-Headers", "*") - c.Response().Header().Set("HX-Push-Url", fmt.Sprintf("/games/%v", g.Id)) + c.Response().Header().Set("HX-Push-Url", fmt.Sprintf("/games/%v", g.ID)) return render(c, http.StatusCreated, details) } +func (h Handler) DeleteGame(c echo.Context) error { + gameId := c.Param("id") + + parsedId, err := strconv.Atoi(gameId) + if err != nil { + return err + } + + err = h.service.Delete(int32(parsedId)) + if err != nil { + c.Logger().Error(err) + return err + } + + return nil +} + func (h Handler) AddRound(c echo.Context) error { gameId := c.Param("id") parsedId, err := strconv.Atoi(gameId) if err != nil { + c.Logger().Error(err) return err } - game := h.service.Find(parsedId) + game, err := h.service.Find(int32(parsedId)) if err != nil { + c.Logger().Error(err) return err } - fmt.Printf("rounds before: %+v", len(game.Rounds)) + var params map[string]string = make(map[string]string) c.Bind(¶ms) round := skat.Round{} + round.GameID = game.ID + round.CreatedAt = time.Now() for _, player := range game.Players { - role, exists := params[player] + role, exists := params[player.Name] if !exists { continue } switch role { case "declarer": - round.Declarer = player + round.Declarer = player.ID case "opponent": - round.Opponents = append(round.Opponents, player) + round.Opponents = append(round.Opponents, player.ID) case "dealer": - round.Dealer = player + round.Dealer = &player.ID } } @@ -98,6 +150,7 @@ func (h Handler) AddRound(c echo.Context) error { if exists { won, err := strconv.ParseBool(wonStr) if err != nil { + c.Logger().Error(err) return err } round.Won = won @@ -107,27 +160,29 @@ func (h Handler) AddRound(c echo.Context) error { if exists { gameValue, err := strconv.Atoi(gameValueStr) if err != nil { + c.Logger().Error(err) return err } - round.Value = gameValue + round.Value = int32(gameValue) + } + + round, err = h.service.AddRound(game.ID, round) + if err != nil { + log.Error(err) + return err } game.Rounds = append(game.Rounds, round) - fmt.Printf("rounds after: %+v", len(game.Rounds)) gameDetails := template.GameDetails(*game) return render(c, http.StatusCreated, gameDetails) } -func (h Handler) GetCreateGameForm(c echo.Context) error { - form := components.CreateGameForm() - return render(c, http.StatusOK, form) -} - func render(ctx echo.Context, status int, t templ.Component) error { ctx.Response().Writer.WriteHeader(status) err := t.Render(context.Background(), ctx.Response().Writer) if err != nil { + ctx.Logger().Error(err) return ctx.String(http.StatusInternalServerError, "failed to render response template") } diff --git a/internal/skat/gen/model/game.go b/internal/skat/gen/model/game.go new file mode 100644 index 0000000..0d96d58 --- /dev/null +++ b/internal/skat/gen/model/game.go @@ -0,0 +1,19 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +import ( + "time" +) + +type Game struct { + ID int32 `sql:"primary_key"` + CreatedAt time.Time + Stake float32 + Online bool +} diff --git a/internal/skat/gen/model/game_player.go b/internal/skat/gen/model/game_player.go new file mode 100644 index 0000000..17f555e --- /dev/null +++ b/internal/skat/gen/model/game_player.go @@ -0,0 +1,14 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type GamePlayer struct { + GameID int32 + PlayerID int32 + Rank int32 +} diff --git a/internal/skat/gen/model/player.go b/internal/skat/gen/model/player.go new file mode 100644 index 0000000..2a62869 --- /dev/null +++ b/internal/skat/gen/model/player.go @@ -0,0 +1,13 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type Player struct { + ID int32 `sql:"primary_key"` + Name string +} diff --git a/internal/skat/gen/model/round.go b/internal/skat/gen/model/round.go new file mode 100644 index 0000000..fb79d6e --- /dev/null +++ b/internal/skat/gen/model/round.go @@ -0,0 +1,22 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +import ( + "time" +) + +type Round struct { + ID int32 `sql:"primary_key"` + GameID int32 + CreatedAt time.Time + Dealer *int32 + Declarer int32 + Won bool + Value int32 +} diff --git a/internal/skat/gen/model/round_opponent.go b/internal/skat/gen/model/round_opponent.go new file mode 100644 index 0000000..afdcf6f --- /dev/null +++ b/internal/skat/gen/model/round_opponent.go @@ -0,0 +1,13 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package model + +type RoundOpponent struct { + RoundID int32 `sql:"primary_key"` + PlayerID int32 `sql:"primary_key"` +} diff --git a/internal/skat/gen/table/game.go b/internal/skat/gen/table/game.go new file mode 100644 index 0000000..d008b46 --- /dev/null +++ b/internal/skat/gen/table/game.go @@ -0,0 +1,84 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var Game = newGameTable("", "game", "") + +type gameTable struct { + sqlite.Table + + // Columns + ID sqlite.ColumnInteger + CreatedAt sqlite.ColumnTimestamp + Stake sqlite.ColumnFloat + Online sqlite.ColumnBool + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type GameTable struct { + gameTable + + EXCLUDED gameTable +} + +// AS creates new GameTable with assigned alias +func (a GameTable) AS(alias string) *GameTable { + return newGameTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new GameTable with assigned schema name +func (a GameTable) FromSchema(schemaName string) *GameTable { + return newGameTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new GameTable with assigned table prefix +func (a GameTable) WithPrefix(prefix string) *GameTable { + return newGameTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new GameTable with assigned table suffix +func (a GameTable) WithSuffix(suffix string) *GameTable { + return newGameTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newGameTable(schemaName, tableName, alias string) *GameTable { + return &GameTable{ + gameTable: newGameTableImpl(schemaName, tableName, alias), + EXCLUDED: newGameTableImpl("", "excluded", ""), + } +} + +func newGameTableImpl(schemaName, tableName, alias string) gameTable { + var ( + IDColumn = sqlite.IntegerColumn("id") + CreatedAtColumn = sqlite.TimestampColumn("created_at") + StakeColumn = sqlite.FloatColumn("stake") + OnlineColumn = sqlite.BoolColumn("online") + allColumns = sqlite.ColumnList{IDColumn, CreatedAtColumn, StakeColumn, OnlineColumn} + mutableColumns = sqlite.ColumnList{CreatedAtColumn, StakeColumn, OnlineColumn} + ) + + return gameTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + ID: IDColumn, + CreatedAt: CreatedAtColumn, + Stake: StakeColumn, + Online: OnlineColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/internal/skat/gen/table/game_player.go b/internal/skat/gen/table/game_player.go new file mode 100644 index 0000000..c9bee13 --- /dev/null +++ b/internal/skat/gen/table/game_player.go @@ -0,0 +1,81 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var GamePlayer = newGamePlayerTable("", "game_player", "") + +type gamePlayerTable struct { + sqlite.Table + + // Columns + GameID sqlite.ColumnInteger + PlayerID sqlite.ColumnInteger + Rank sqlite.ColumnInteger + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type GamePlayerTable struct { + gamePlayerTable + + EXCLUDED gamePlayerTable +} + +// AS creates new GamePlayerTable with assigned alias +func (a GamePlayerTable) AS(alias string) *GamePlayerTable { + return newGamePlayerTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new GamePlayerTable with assigned schema name +func (a GamePlayerTable) FromSchema(schemaName string) *GamePlayerTable { + return newGamePlayerTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new GamePlayerTable with assigned table prefix +func (a GamePlayerTable) WithPrefix(prefix string) *GamePlayerTable { + return newGamePlayerTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new GamePlayerTable with assigned table suffix +func (a GamePlayerTable) WithSuffix(suffix string) *GamePlayerTable { + return newGamePlayerTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newGamePlayerTable(schemaName, tableName, alias string) *GamePlayerTable { + return &GamePlayerTable{ + gamePlayerTable: newGamePlayerTableImpl(schemaName, tableName, alias), + EXCLUDED: newGamePlayerTableImpl("", "excluded", ""), + } +} + +func newGamePlayerTableImpl(schemaName, tableName, alias string) gamePlayerTable { + var ( + GameIDColumn = sqlite.IntegerColumn("game_id") + PlayerIDColumn = sqlite.IntegerColumn("player_id") + RankColumn = sqlite.IntegerColumn("rank") + allColumns = sqlite.ColumnList{GameIDColumn, PlayerIDColumn, RankColumn} + mutableColumns = sqlite.ColumnList{GameIDColumn, PlayerIDColumn, RankColumn} + ) + + return gamePlayerTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + GameID: GameIDColumn, + PlayerID: PlayerIDColumn, + Rank: RankColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/internal/skat/gen/table/player.go b/internal/skat/gen/table/player.go new file mode 100644 index 0000000..eb3bd88 --- /dev/null +++ b/internal/skat/gen/table/player.go @@ -0,0 +1,78 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var Player = newPlayerTable("", "player", "") + +type playerTable struct { + sqlite.Table + + // Columns + ID sqlite.ColumnInteger + Name sqlite.ColumnString + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type PlayerTable struct { + playerTable + + EXCLUDED playerTable +} + +// AS creates new PlayerTable with assigned alias +func (a PlayerTable) AS(alias string) *PlayerTable { + return newPlayerTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new PlayerTable with assigned schema name +func (a PlayerTable) FromSchema(schemaName string) *PlayerTable { + return newPlayerTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new PlayerTable with assigned table prefix +func (a PlayerTable) WithPrefix(prefix string) *PlayerTable { + return newPlayerTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new PlayerTable with assigned table suffix +func (a PlayerTable) WithSuffix(suffix string) *PlayerTable { + return newPlayerTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newPlayerTable(schemaName, tableName, alias string) *PlayerTable { + return &PlayerTable{ + playerTable: newPlayerTableImpl(schemaName, tableName, alias), + EXCLUDED: newPlayerTableImpl("", "excluded", ""), + } +} + +func newPlayerTableImpl(schemaName, tableName, alias string) playerTable { + var ( + IDColumn = sqlite.IntegerColumn("id") + NameColumn = sqlite.StringColumn("name") + allColumns = sqlite.ColumnList{IDColumn, NameColumn} + mutableColumns = sqlite.ColumnList{NameColumn} + ) + + return playerTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + ID: IDColumn, + Name: NameColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/internal/skat/gen/table/round.go b/internal/skat/gen/table/round.go new file mode 100644 index 0000000..0e40b36 --- /dev/null +++ b/internal/skat/gen/table/round.go @@ -0,0 +1,93 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var Round = newRoundTable("", "round", "") + +type roundTable struct { + sqlite.Table + + // Columns + ID sqlite.ColumnInteger + GameID sqlite.ColumnInteger + CreatedAt sqlite.ColumnTimestamp + Dealer sqlite.ColumnInteger + Declarer sqlite.ColumnInteger + Won sqlite.ColumnBool + Value sqlite.ColumnInteger + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type RoundTable struct { + roundTable + + EXCLUDED roundTable +} + +// AS creates new RoundTable with assigned alias +func (a RoundTable) AS(alias string) *RoundTable { + return newRoundTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new RoundTable with assigned schema name +func (a RoundTable) FromSchema(schemaName string) *RoundTable { + return newRoundTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new RoundTable with assigned table prefix +func (a RoundTable) WithPrefix(prefix string) *RoundTable { + return newRoundTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new RoundTable with assigned table suffix +func (a RoundTable) WithSuffix(suffix string) *RoundTable { + return newRoundTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newRoundTable(schemaName, tableName, alias string) *RoundTable { + return &RoundTable{ + roundTable: newRoundTableImpl(schemaName, tableName, alias), + EXCLUDED: newRoundTableImpl("", "excluded", ""), + } +} + +func newRoundTableImpl(schemaName, tableName, alias string) roundTable { + var ( + IDColumn = sqlite.IntegerColumn("id") + GameIDColumn = sqlite.IntegerColumn("game_id") + CreatedAtColumn = sqlite.TimestampColumn("created_at") + DealerColumn = sqlite.IntegerColumn("dealer") + DeclarerColumn = sqlite.IntegerColumn("declarer") + WonColumn = sqlite.BoolColumn("won") + ValueColumn = sqlite.IntegerColumn("value") + allColumns = sqlite.ColumnList{IDColumn, GameIDColumn, CreatedAtColumn, DealerColumn, DeclarerColumn, WonColumn, ValueColumn} + mutableColumns = sqlite.ColumnList{GameIDColumn, CreatedAtColumn, DealerColumn, DeclarerColumn, WonColumn, ValueColumn} + ) + + return roundTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + ID: IDColumn, + GameID: GameIDColumn, + CreatedAt: CreatedAtColumn, + Dealer: DealerColumn, + Declarer: DeclarerColumn, + Won: WonColumn, + Value: ValueColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/internal/skat/gen/table/round_opponent.go b/internal/skat/gen/table/round_opponent.go new file mode 100644 index 0000000..129d50b --- /dev/null +++ b/internal/skat/gen/table/round_opponent.go @@ -0,0 +1,78 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +import ( + "github.com/go-jet/jet/v2/sqlite" +) + +var RoundOpponent = newRoundOpponentTable("", "round_opponent", "") + +type roundOpponentTable struct { + sqlite.Table + + // Columns + RoundID sqlite.ColumnInteger + PlayerID sqlite.ColumnInteger + + AllColumns sqlite.ColumnList + MutableColumns sqlite.ColumnList +} + +type RoundOpponentTable struct { + roundOpponentTable + + EXCLUDED roundOpponentTable +} + +// AS creates new RoundOpponentTable with assigned alias +func (a RoundOpponentTable) AS(alias string) *RoundOpponentTable { + return newRoundOpponentTable(a.SchemaName(), a.TableName(), alias) +} + +// Schema creates new RoundOpponentTable with assigned schema name +func (a RoundOpponentTable) FromSchema(schemaName string) *RoundOpponentTable { + return newRoundOpponentTable(schemaName, a.TableName(), a.Alias()) +} + +// WithPrefix creates new RoundOpponentTable with assigned table prefix +func (a RoundOpponentTable) WithPrefix(prefix string) *RoundOpponentTable { + return newRoundOpponentTable(a.SchemaName(), prefix+a.TableName(), a.TableName()) +} + +// WithSuffix creates new RoundOpponentTable with assigned table suffix +func (a RoundOpponentTable) WithSuffix(suffix string) *RoundOpponentTable { + return newRoundOpponentTable(a.SchemaName(), a.TableName()+suffix, a.TableName()) +} + +func newRoundOpponentTable(schemaName, tableName, alias string) *RoundOpponentTable { + return &RoundOpponentTable{ + roundOpponentTable: newRoundOpponentTableImpl(schemaName, tableName, alias), + EXCLUDED: newRoundOpponentTableImpl("", "excluded", ""), + } +} + +func newRoundOpponentTableImpl(schemaName, tableName, alias string) roundOpponentTable { + var ( + RoundIDColumn = sqlite.IntegerColumn("round_id") + PlayerIDColumn = sqlite.IntegerColumn("player_id") + allColumns = sqlite.ColumnList{RoundIDColumn, PlayerIDColumn} + mutableColumns = sqlite.ColumnList{} + ) + + return roundOpponentTable{ + Table: sqlite.NewTable(schemaName, tableName, alias, allColumns...), + + //Columns + RoundID: RoundIDColumn, + PlayerID: PlayerIDColumn, + + AllColumns: allColumns, + MutableColumns: mutableColumns, + } +} diff --git a/internal/skat/gen/table/table_use_schema.go b/internal/skat/gen/table/table_use_schema.go new file mode 100644 index 0000000..3fa1be0 --- /dev/null +++ b/internal/skat/gen/table/table_use_schema.go @@ -0,0 +1,18 @@ +// +// Code generated by go-jet DO NOT EDIT. +// +// WARNING: Changes to this file may cause incorrect behavior +// and will be lost if the code is regenerated +// + +package table + +// UseSchema sets a new schema name for all generated table SQL builder types. It is recommended to invoke +// this method only once at the beginning of the program. +func UseSchema(schema string) { + Game = Game.FromSchema(schema) + GamePlayer = GamePlayer.FromSchema(schema) + Player = Player.FromSchema(schema) + Round = Round.FromSchema(schema) + RoundOpponent = RoundOpponent.FromSchema(schema) +} diff --git a/internal/skat/service.go b/internal/skat/service.go index 85251ee..3d90a86 100644 --- a/internal/skat/service.go +++ b/internal/skat/service.go @@ -1,62 +1,250 @@ package skat import ( - "math/rand" - "time" + "database/sql" + "fmt" + "slices" + + "github.com/go-jet/jet/v2/sqlite" + "github.com/tarow/skat-counter/internal/skat/gen/model" + "github.com/tarow/skat-counter/internal/skat/gen/table" ) type Service struct { + db *sql.DB } -func NewService() Service { - return Service{} +func NewService(db *sql.DB) Service { + return Service{db: db} } -var DB = []Game{ - { - Id: 0, - Started: time.Now().AddDate(0, 0, -1), - Stake: 2, - Ended: nil, - Players: []string{"Jannik", "Moritz", "Manuel", "Niklas"}, - Online: false, - Rounds: []Round{ - { - Dealer: "Jannik", - Declarer: "Moritz", - Opponents: []string{"Niklas", "Manuel"}, - Won: true, - Value: 24, - }, - { - Dealer: "Moritz", - Declarer: "Manuel", - Opponents: []string{"Jannik", "Niklas"}, - Won: false, - Value: 24, - }, - }, - }, +func (s Service) List() ([]Game, error) { + stmt := sqlite.SELECT( + table.Game.AllColumns, + table.Player.AllColumns, + table.Round.AllColumns, + table.RoundOpponent.PlayerID.AS("opponent.id"), + ).FROM(table.Game. + INNER_JOIN(table.GamePlayer, table.Game.ID.EQ(table.GamePlayer.GameID)). + INNER_JOIN(table.Player, table.Player.ID.EQ(table.GamePlayer.PlayerID)). + LEFT_JOIN(table.Round, table.Round.GameID.EQ(table.Game.ID)). + LEFT_JOIN(table.RoundOpponent, table.RoundOpponent.RoundID.EQ(table.Round.ID).AND( + table.RoundOpponent.PlayerID.EQ(table.Player.ID), + )), + ).ORDER_BY(table.Game.CreatedAt.DESC(), table.GamePlayer.Rank.ASC(), table.Round.CreatedAt.ASC()) + + games := make([]Game, 0) + err := stmt.Query(s.db, &games) + if err != nil { + return []Game{}, err + } + + return games, nil } -func (s Service) List() []Game { - return DB +func (s Service) Find(gameId int32) (*Game, error) { + stmt := sqlite.SELECT( + table.Game.AllColumns, + table.Player.AllColumns, + table.Round.AllColumns, + table.RoundOpponent.PlayerID.AS("opponent.id"), + ).FROM(table.Game. + INNER_JOIN(table.GamePlayer, table.Game.ID.EQ(table.GamePlayer.GameID)). + INNER_JOIN(table.Player, table.Player.ID.EQ(table.GamePlayer.PlayerID)). + LEFT_JOIN(table.Round, table.Round.GameID.EQ(table.Game.ID)). + LEFT_JOIN(table.RoundOpponent, table.RoundOpponent.RoundID.EQ(table.Round.ID).AND( + table.RoundOpponent.PlayerID.EQ(table.Player.ID), + )), + ).WHERE(table.Game.ID.EQ(sqlite.Int32(gameId))). + ORDER_BY(table.GamePlayer.Rank.ASC(), table.Round.CreatedAt.ASC()) + + game := Game{} + err := stmt.Query(s.db, &game) + fmt.Println(stmt.DebugSql()) + if err != nil { + return &Game{}, err + } + + return &game, nil } -func (s Service) Find(gameId int) *Game { - for i, e := range DB { - if gameId == e.Id { - return &DB[i] - } +func (s Service) Create(g Game) (game Game, err error) { + tx, err := s.db.Begin() + if err != nil { + return Game{}, err } - return nil + + defer tx.Rollback() + + // Create players if they dont exist + stmt := table.Player. + INSERT(table.Player.Name). + MODELS(g.Players). + ON_CONFLICT().DO_NOTHING().RETURNING(table.Player.AllColumns) + + _, err = stmt.Exec(tx) + if err != nil { + + return Game{}, err + } + + // Insert game + stmt = table.Game.INSERT( + table.Game.Online, + table.Game.Stake, + table.Game.CreatedAt, + ).MODEL(g) + + res, err := stmt.Exec(tx) + if err != nil { + return Game{}, err + } + + gameId, err := res.LastInsertId() + if err != nil { + return Game{}, err + } + + playerNames := []sqlite.Expression{} + playerNamesStr := []string{} + for _, p := range g.Players { + playerNames = append(playerNames, sqlite.String(p.Name)) + playerNamesStr = append(playerNamesStr, p.Name) + } + + // Link players to game + selectPlayers := table.Player.SELECT(table.Player.AllColumns). + FROM(table.Player). + WHERE(table.Player.Name.IN(playerNames...)) + + players := make([]model.Player, 0) + err = selectPlayers.Query(tx, &players) + if err != nil { + return Game{}, err + } + + gamePlayers := []model.GamePlayer{} + for _, p := range players { + gamePlayers = append(gamePlayers, model.GamePlayer{ + GameID: int32(gameId), + PlayerID: p.ID, + Rank: int32(slices.Index(playerNamesStr, p.Name)), + }) + } + stmt = table.GamePlayer.INSERT(table.GamePlayer.AllColumns). + MODELS(gamePlayers) + _, err = stmt.Exec(tx) + if err != nil { + return Game{}, err + } + + err = tx.Commit() + if err != nil { + return Game{}, err + } + + g.ID = int32(gameId) + return g, nil } -func (s Service) Create(g Game) Game { - g.Id = rand.Intn(100) - g.Started = time.Now() - g.Ended = nil +func (s Service) AddRound(gameId int32, round Round) (Round, error) { + opponents := round.Opponents - DB = append(DB, g) - return g + tx, err := s.db.Begin() + if err != nil { + return Round{}, err + } + defer tx.Rollback() + + // Insert round + stmt := table.Round.INSERT( + table.Round.AllColumns.Except(table.Round.ID), + ).MODEL(round).RETURNING(table.Round.AllColumns) + + err = stmt.Query(tx, &round) + if err != nil { + return Round{}, err + } + round.Opponents = opponents + + roundOpponents := make([]model.RoundOpponent, 0) + for _, opponentId := range opponents { + roundOpponents = append(roundOpponents, model.RoundOpponent{ + RoundID: round.ID, + PlayerID: opponentId, + }) + } + stmt = table.RoundOpponent.INSERT( + table.RoundOpponent.AllColumns, + ).MODELS(roundOpponents) + + _, err = stmt.Exec(tx) + if err != nil { + return Round{}, err + } + + err = tx.Commit() + if err != nil { + return Round{}, err + } + + return round, nil +} + +func (s Service) Delete(gameId int32) error { + tx, err := s.db.Begin() + if err != nil { + return err + } + defer tx.Rollback() + + // Delete round opponents + stmt := table.RoundOpponent.DELETE().WHERE( + table.RoundOpponent.RoundID.IN( + table.Round.SELECT(table.Round.ID).FROM(table.Round).WHERE( + table.Round.GameID.EQ(sqlite.Int32(gameId)), + ), + )) + + _, err = stmt.Exec(tx) + if err != nil { + return err + } + + //Delete rounds + stmt = table.Round.DELETE().WHERE(table.Round.GameID.EQ(sqlite.Int32(gameId))) + _, err = stmt.Exec(tx) + if err != nil { + return err + } + + //Delete game players references + stmt = table.GamePlayer.DELETE().WHERE(table.GamePlayer.GameID.EQ(sqlite.Int32(gameId))) + _, err = stmt.Exec(tx) + if err != nil { + return err + } + + //Delete game + stmt = table.Game.DELETE().WHERE(table.Game.ID.EQ(sqlite.Int32(gameId))) + _, err = stmt.Exec(tx) + if err != nil { + return err + } + + // Delete orphan players that arent refenced in any game + stmt = table.Player.DELETE().WHERE(table.Player.ID.NOT_IN( + table.GamePlayer.SELECT(table.GamePlayer.PlayerID), + )) + _, err = stmt.Exec(tx) + if err != nil { + return err + } + + err = tx.Commit() + if err != nil { + return err + } + + return nil } diff --git a/internal/skat/skat.go b/internal/skat/skat.go index 4529f80..53526b4 100644 --- a/internal/skat/skat.go +++ b/internal/skat/skat.go @@ -2,37 +2,27 @@ package skat import ( "slices" - "time" + + "github.com/tarow/skat-counter/internal/skat/gen/model" ) type Game struct { - Id int - Started time.Time - Ended *time.Time - Stake float32 `form:"stake"` - Players []string `form:"player"` - Rounds []Round `form:"round"` - Online bool `form:"online"` + model.Game + Players []model.Player + Rounds []Round } type Round struct { - Dealer string - Declarer string - Opponents []string - Won bool - Value int -} - -func (g Game) IsActive() bool { - return g.Ended == nil || g.Ended.After(time.Now()) + model.Round + Opponents []int32 `alias:"opponent.id"` } func (g Game) GetDate() string { - return g.Started.Format("Monday, 02.01.2006") + return g.CreatedAt.Format("Monday, 02.01.2006") } -func (g Game) GetTotalPlayerScore(player string) int { - sum := 0 +func (g Game) GetTotalPlayerScore(player model.Player) int32 { + sum := int32(0) for _, r := range g.Rounds { roundScore := r.GetRoundScore(player) if roundScore != nil { @@ -43,7 +33,7 @@ func (g Game) GetTotalPlayerScore(player string) int { } func (g Game) GetTotalPayment() float32 { - sum := 0 + sum := int32(0) for _, player := range g.Players { sum += g.GetTotalPlayerScore(player) } @@ -51,8 +41,8 @@ func (g Game) GetTotalPayment() float32 { return float32(sum) * g.Stake } -func (r Round) GetRoundScore(player string) *int { - if player == r.Declarer { +func (r Round) GetRoundScore(player model.Player) *int32 { + if player.ID == r.Declarer { if r.Won { return intPtr(0) } else { @@ -60,7 +50,7 @@ func (r Round) GetRoundScore(player string) *int { } } - if slices.Contains(r.Opponents, player) { + if slices.Contains(r.Opponents, player.ID) { if r.Won { return &r.Value } else { @@ -71,6 +61,6 @@ func (r Round) GetRoundScore(player string) *int { return nil } -func intPtr(i int) *int { +func intPtr(i int32) *int32 { return &i } diff --git a/main.go b/main.go index 75330de..3a0ce59 100644 --- a/main.go +++ b/main.go @@ -1,7 +1,11 @@ package main import ( + "database/sql" "embed" + "log" + + _ "modernc.org/sqlite" "github.com/labstack/echo/v4" api "github.com/tarow/skat-counter/internal/api" @@ -12,9 +16,17 @@ import ( var staticAssets embed.FS func main() { + db, err := sql.Open("sqlite", "skat.sqlite") + createTables(db) + + if err != nil { + log.Fatal(err) + } + defer db.Close() + e := echo.New() - handler := api.NewHandler(skat.NewService()) + handler := api.NewHandler(skat.NewService(db)) registerRoutes(e, handler) e.Logger.Fatal(e.Start(":8080")) @@ -30,6 +42,53 @@ func registerRoutes(e *echo.Echo, handler api.Handler) { e.POST("/games", handler.CreateGame) e.GET("/games/:id", handler.GetGameDetails) + e.DELETE("/games/:id", handler.DeleteGame) e.POST("/games/:id/rounds", handler.AddRound) } +func createTables(db *sql.DB) error { + _, err := db.Exec(` + CREATE TABLE IF NOT EXISTS player ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + name TEXT UNIQUE NOT NULL + ); + + CREATE TABLE IF NOT EXISTS game ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + created_at TIMESTAMP NOT NULL, + stake REAL NOT NULL, + online BOOLEAN NOT NULL + ); + + CREATE TABLE IF NOT EXISTS game_player ( + game_id INTEGER NOT NULL, + player_id INTEGER NOT NULL, + rank INTEGER NOT NULL, + FOREIGN KEY(game_id) REFERENCES games(id), + FOREIGN KEY(player_id) REFERENCES players(id) + ); + + CREATE TABLE IF NOT EXISTS round ( + id INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, + game_id INTEGER NOT NULL, + created_at TIMESTAMP NOT NULL, + dealer INTEGER, + declarer INTEGER NOT NULL, + won BOOLEAN NOT NULL, + value INTEGER NOT NULL, + FOREIGN KEY(game_id) REFERENCES games(id), + FOREIGN KEY(dealer) REFERENCES players(id), + FOREIGN KEY(declarer) REFERENCES players(id) + ); + + CREATE TABLE IF NOT EXISTS round_opponent ( + round_id INTEGER NOT NULL, + player_id INTEGER NOT NULL, + PRIMARY KEY (round_id, player_id), + FOREIGN KEY(round_id) REFERENCES rounds(id), + FOREIGN KEY(player_id) REFERENCES players(id) + ); + `) + + return err +} diff --git a/templates/GameDetails.templ b/templates/GameDetails.templ index df7610c..c521f51 100644 --- a/templates/GameDetails.templ +++ b/templates/GameDetails.templ @@ -20,7 +20,7 @@ templ GameDetails (game skat.Game) {
@components.RoundsList(game)
- @components.Game(game, false) + @components.GameCard(game, false) @components.AddRoundForm(game)
diff --git a/templates/GameDetails_templ.go b/templates/GameDetails_templ.go index 9d4a522..a23e929 100644 --- a/templates/GameDetails_templ.go +++ b/templates/GameDetails_templ.go @@ -81,7 +81,7 @@ func GameDetails(game skat.Game) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - templ_7745c5c3_Err = components.Game(game, false).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = components.GameCard(game, false).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/templates/GameOverview.templ b/templates/GameOverview.templ index 4b346ff..0db299b 100644 --- a/templates/GameOverview.templ +++ b/templates/GameOverview.templ @@ -12,7 +12,7 @@ templ GameOverviewNavbar() { @components.CreateGameForm() diff --git a/templates/GameOverview_templ.go b/templates/GameOverview_templ.go index 806ec71..97feabe 100644 --- a/templates/GameOverview_templ.go +++ b/templates/GameOverview_templ.go @@ -35,7 +35,7 @@ func GameOverviewNavbar() templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" } @@ -72,7 +77,7 @@ templ GameList(games []skat.Game) {
for _, g := range games { - @Game(g, true) + @GameCard(g, true) }
diff --git a/templates/components/gamelist_templ.go b/templates/components/game_card_templ.go similarity index 86% rename from templates/components/gamelist_templ.go rename to templates/components/game_card_templ.go index 16193f2..413e1f0 100644 --- a/templates/components/gamelist_templ.go +++ b/templates/components/game_card_templ.go @@ -13,7 +13,7 @@ import "bytes" import "github.com/tarow/skat-counter/internal/skat" import "fmt" -func Game(game skat.Game, showFooter bool) templ.Component { +func GameCard(game skat.Game, showFooter bool) templ.Component { return templ.ComponentFunc(func(ctx context.Context, templ_7745c5c3_W io.Writer) (templ_7745c5c3_Err error) { templ_7745c5c3_Buffer, templ_7745c5c3_IsBuffer := templ_7745c5c3_W.(*bytes.Buffer) if !templ_7745c5c3_IsBuffer { @@ -61,7 +61,7 @@ func Game(game skat.Game, showFooter bool) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var5 string = p + var templ_7745c5c3_Var5 string = p.Name _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var5)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -112,11 +112,11 @@ func Game(game skat.Game, showFooter bool) templ.Component { return templ_7745c5c3_Err } if showFooter { - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(" ") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -164,7 +172,7 @@ func GameList(games []skat.Game) templ.Component { return templ_7745c5c3_Err } for _, g := range games { - templ_7745c5c3_Err = Game(g, true).Render(ctx, templ_7745c5c3_Buffer) + templ_7745c5c3_Err = GameCard(g, true).Render(ctx, templ_7745c5c3_Buffer) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } diff --git a/templates/components/rounds_list.templ b/templates/components/rounds_list.templ index 6b7845f..fe1882a 100644 --- a/templates/components/rounds_list.templ +++ b/templates/components/rounds_list.templ @@ -5,13 +5,13 @@ import "fmt" templ RoundsList(game skat.Game) {
- +
- + for _, player := range game.Players { - + } @@ -27,7 +27,7 @@ templ RoundsList(game skat.Game) { } - + for _, player := range game.Players { @@ -56,9 +56,9 @@ templ AddRoundForm(game skat.Game) { for _, player := range game.Players {
{ player }{ player.Name }Value
Total
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("-->") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -44,7 +44,7 @@ func RoundsList(game skat.Game) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var3 string = player + var templ_7745c5c3_Var3 string = player.Name _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var3)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -110,7 +110,7 @@ func RoundsList(game skat.Game) templ.Component { return templ_7745c5c3_Err } } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString("
") if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -222,7 +222,7 @@ func AddRoundForm(game skat.Game) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - var templ_7745c5c3_Var16 string = player + var templ_7745c5c3_Var16 string = player.Name _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(templ_7745c5c3_Var16)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err @@ -231,7 +231,7 @@ func AddRoundForm(game skat.Game) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(player)) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(player.Name)) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -298,7 +298,7 @@ func AddRoundForm(game skat.Game) templ.Component { if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } - _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("/games/%v/rounds", game.Id))) + _, templ_7745c5c3_Err = templ_7745c5c3_Buffer.WriteString(templ.EscapeString(fmt.Sprintf("/games/%v/rounds", game.ID))) if templ_7745c5c3_Err != nil { return templ_7745c5c3_Err } @@ -322,7 +322,7 @@ func AddRoundForm(game skat.Game) templ.Component { }) } -func formatRoundScore(score *int) string { +func formatRoundScore(score *int32) string { if score == nil { return "-" }