configuration baby

This commit is contained in:
Jakub Kropáček 2024-11-23 18:02:41 +01:00
parent 888244daaa
commit 8c0d983297
8 changed files with 315 additions and 33 deletions

View file

@ -10,7 +10,7 @@ Why? Because I can. And because I wanted to learn how to write a web application
- [x] Web server is up and running - [x] Web server is up and running
- [x] Basic API routes - [x] Basic API routes
- [x] Database migrations (only for sqlite) - [x] Database migrations (only for sqlite)
- [ ] Configurable database connection - [x] Configurable database connection
- [ ] Automatic database migrations (will be probably only supported for Docker deployments or with a guide) - [ ] Automatic database migrations (will be probably only supported for Docker deployments or with a guide)
- [ ] Tests - [ ] Tests
- [ ] Frontend using templates - [ ] Frontend using templates

View file

@ -2,23 +2,50 @@ package main
import ( import (
"database/sql" "database/sql"
"flag"
"git.katuwoss.dev/JustScreaMy/secret-santa/internal/app" "git.katuwoss.dev/JustScreaMy/secret-santa/internal/app"
"git.katuwoss.dev/JustScreaMy/secret-santa/internal/config" "git.katuwoss.dev/JustScreaMy/secret-santa/internal/config"
_ "github.com/go-sql-driver/mysql"
_ "github.com/jackc/pgx/v5/stdlib"
_ "github.com/mattn/go-sqlite3" _ "github.com/mattn/go-sqlite3"
"log" "log"
"os"
) )
func main() { var (
dbConnection, err := sql.Open("sqlite3", "./test.db") configPath string
if err != nil { )
log.Fatal(err)
func init() {
flag.StringVar(&configPath, "config", "config.yaml", "path to config file")
flag.Parse()
// Check if the config file exists
if _, err := os.Stat(configPath); os.IsNotExist(err) {
log.Fatalf("Config file not found: %s", configPath)
} }
log.Printf("Using config file: %s", configPath)
}
func main() {
appConfig, err := config.ParseConfig(configPath)
if err != nil {
log.Fatalf("Failed to parse config: %s", err.Error())
}
dbConnection, err := sql.Open(appConfig.DB.GetDriver(), appConfig.DB.GenerateDSN())
if err != nil {
log.Fatalf("Database connection failed: %s", err.Error())
}
if dbConnection.Ping() != nil {
log.Fatalf("Database connection failed: unable to ping database")
}
log.Println("Database sucessfully connected") log.Println("Database sucessfully connected")
application := app.NewApp(dbConnection, &appConfig.App, nil)
appConfig := config.NewAppConfig(nil, nil) log.Printf("Listening on http://%s", appConfig.App.GenerateIP())
application := app.NewApp(dbConnection, &appConfig, nil)
log.Printf("Listening on http://%s", appConfig.GenerateIP())
log.Fatal(application.Start()) log.Fatal(application.Start())
} }

14
go.mod
View file

@ -3,6 +3,8 @@ module git.katuwoss.dev/JustScreaMy/secret-santa
go 1.23.2 go 1.23.2
require ( require (
github.com/go-sql-driver/mysql v1.6.0
github.com/jackc/pgx/v5 v5.5.4
github.com/mattn/go-sqlite3 v1.14.24 github.com/mattn/go-sqlite3 v1.14.24
github.com/rubenv/sql-migrate v1.7.0 github.com/rubenv/sql-migrate v1.7.0
github.com/stretchr/testify v1.9.0 github.com/stretchr/testify v1.9.0
@ -25,13 +27,21 @@ require (
github.com/docker/go-connections v0.5.0 // indirect github.com/docker/go-connections v0.5.0 // indirect
github.com/docker/go-units v0.5.0 // indirect github.com/docker/go-units v0.5.0 // indirect
github.com/felixge/httpsnoop v1.0.4 // indirect github.com/felixge/httpsnoop v1.0.4 // indirect
github.com/gabriel-vasile/mimetype v1.4.3 // indirect
github.com/go-gorp/gorp/v3 v3.1.0 // indirect github.com/go-gorp/gorp/v3 v3.1.0 // indirect
github.com/go-logr/logr v1.4.1 // indirect github.com/go-logr/logr v1.4.1 // indirect
github.com/go-logr/stdr v1.2.2 // indirect github.com/go-logr/stdr v1.2.2 // indirect
github.com/go-ole/go-ole v1.2.6 // indirect github.com/go-ole/go-ole v1.2.6 // indirect
github.com/go-playground/locales v0.14.1 // indirect
github.com/go-playground/universal-translator v0.18.1 // indirect
github.com/go-playground/validator/v10 v10.23.0 // indirect
github.com/gogo/protobuf v1.3.2 // indirect github.com/gogo/protobuf v1.3.2 // indirect
github.com/google/uuid v1.6.0 // indirect github.com/google/uuid v1.6.0 // indirect
github.com/jackc/pgpassfile v1.0.0 // indirect
github.com/jackc/pgservicefile v0.0.0-20221227161230-091c0ba34f0a // indirect
github.com/jackc/puddle/v2 v2.2.1 // indirect
github.com/klauspost/compress v1.17.4 // indirect github.com/klauspost/compress v1.17.4 // indirect
github.com/leodido/go-urn v1.4.0 // indirect
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 // indirect
github.com/magiconair/properties v1.8.7 // indirect github.com/magiconair/properties v1.8.7 // indirect
github.com/moby/docker-image-spec v1.3.1 // indirect github.com/moby/docker-image-spec v1.3.1 // indirect
@ -42,6 +52,7 @@ require (
github.com/morikuni/aec v1.0.0 // indirect github.com/morikuni/aec v1.0.0 // indirect
github.com/opencontainers/go-digest v1.0.0 // indirect github.com/opencontainers/go-digest v1.0.0 // indirect
github.com/opencontainers/image-spec v1.1.0 // indirect github.com/opencontainers/image-spec v1.1.0 // indirect
github.com/pelletier/go-toml/v2 v2.2.3 // indirect
github.com/pkg/errors v0.9.1 // indirect github.com/pkg/errors v0.9.1 // indirect
github.com/pmezard/go-difflib v1.0.0 // indirect github.com/pmezard/go-difflib v1.0.0 // indirect
github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect github.com/power-devops/perfstat v0.0.0-20210106213030-5aafc221ea8c // indirect
@ -56,6 +67,9 @@ require (
go.opentelemetry.io/otel/metric v1.24.0 // indirect go.opentelemetry.io/otel/metric v1.24.0 // indirect
go.opentelemetry.io/otel/trace v1.24.0 // indirect go.opentelemetry.io/otel/trace v1.24.0 // indirect
golang.org/x/crypto v0.24.0 // indirect golang.org/x/crypto v0.24.0 // indirect
golang.org/x/net v0.26.0 // indirect
golang.org/x/sync v0.7.0 // indirect
golang.org/x/sys v0.21.0 // indirect golang.org/x/sys v0.21.0 // indirect
golang.org/x/text v0.16.0 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect gopkg.in/yaml.v3 v3.0.1 // indirect
) )

14
go.sum
View file

@ -31,6 +31,8 @@ github.com/docker/go-units v0.5.0 h1:69rxXcBk27SvSaaxTtLh/8llcHD8vYHT7WSdRZ/jvr4
github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk= github.com/docker/go-units v0.5.0/go.mod h1:fgPhTUdO+D/Jk86RDLlptpiXQzgHJF7gydDDbaIK4Dk=
github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg= github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U= github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
github.com/gabriel-vasile/mimetype v1.4.3 h1:in2uUcidCuFcDKtdcBxlR0rJ1+fsokWf+uqxgUFjbI0=
github.com/gabriel-vasile/mimetype v1.4.3/go.mod h1:d8uq/6HKRL6CGdk+aubisF/M5GcPfT7nKyLpA0lbSSk=
github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs= github.com/go-gorp/gorp/v3 v3.1.0 h1:ItKF/Vbuj31dmV4jxA1qblpSwkl9g1typ24xoe70IGs=
github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw= github.com/go-gorp/gorp/v3 v3.1.0/go.mod h1:dLEjIyyRNiXvNZ8PSmzpt1GsWAUK8kjVhEpjH8TixEw=
github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A= github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
@ -40,6 +42,12 @@ 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-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY= github.com/go-ole/go-ole v1.2.6 h1:/Fpf6oFPoeFik9ty7siob0G6Ke8QvQEuVcuChpwXzpY=
github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0= github.com/go-ole/go-ole v1.2.6/go.mod h1:pprOEPIfldk/42T2oK7lQ4v4JSDwmV0As9GaiUsvbm0=
github.com/go-playground/locales v0.14.1 h1:EWaQ/wswjilfKLTECiXz7Rh+3BjFhfDFKv/oXslEjJA=
github.com/go-playground/locales v0.14.1/go.mod h1:hxrqLVvrK65+Rwrd5Fc6F2O76J/NuW9t0sjnWqG1slY=
github.com/go-playground/universal-translator v0.18.1 h1:Bcnm0ZwsGyWbCzImXv+pAJnYK9S473LQFuzCbDbfSFY=
github.com/go-playground/universal-translator v0.18.1/go.mod h1:xekY+UJKNuX9WP91TpwSH2VMlDf28Uj24BCp08ZFTUY=
github.com/go-playground/validator/v10 v10.23.0 h1:/PwmTwZhS0dPkav3cdK9kV1FsAmrL8sThn8IHr/sO+o=
github.com/go-playground/validator/v10 v10.23.0/go.mod h1:dbuPbCMFw/DrkbEynArYaCwl3amGuJotoKCe95atGMM=
github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE= github.com/go-sql-driver/mysql v1.6.0 h1:BCTh4TKNUYmOmMUcQ3IipzF5prigylS7XXjEkfCHuOE=
github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg= github.com/go-sql-driver/mysql v1.6.0/go.mod h1:DCzpHaOWr8IXmIStZouvnhqoel9Qv2LBy8hT2VhHyBg=
github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q= github.com/gogo/protobuf v1.3.2 h1:Ov1cvc58UF3b5XjBnZv7+opcTcQFZebYjWzi34vdm4Q=
@ -68,6 +76,8 @@ github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk= github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY= github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE= github.com/kr/text v0.2.0/go.mod h1:eLer722TekiGuMkidMxC/pM04lWEeraHUUmBw8l2grE=
github.com/leodido/go-urn v1.4.0 h1:WT9HwE9SGECu3lg4d/dIA+jxlljEa1/ffXKmRjqdmIQ=
github.com/leodido/go-urn v1.4.0/go.mod h1:bvxc+MVxLKB4z00jd1z+Dvzr47oO32F/QSNjSBOlFxI=
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw= github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o= github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4= github.com/lufia/plan9stats v0.0.0-20211012122336-39d0f177ccd0 h1:6E+4a0GO5zZEnZ81pIr0yLvtUWk2if982qA3F3QD6H4=
@ -92,6 +102,9 @@ github.com/opencontainers/go-digest v1.0.0 h1:apOUWs51W5PlhuyGyz9FCeeBIOUDA/6nW8
github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM= github.com/opencontainers/go-digest v1.0.0/go.mod h1:0JzlMkj0TRzQZfJkVvzbP0HBR3IKzErnv2BNG4W4MAM=
github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug= github.com/opencontainers/image-spec v1.1.0 h1:8SG7/vwALn54lVB/0yZ/MMwhFrPYtpEHQb2IpWsCzug=
github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM= github.com/opencontainers/image-spec v1.1.0/go.mod h1:W4s4sFTMaBeK1BQLXbG4AdM2szdn85PY75RI83NrTrM=
github.com/pelletier/go-toml v1.9.5 h1:4yBQzkHv+7BHq2PQUZF3Mx0IYxG7LsP222s7Agd3ve8=
github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNHvL12M=
github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM= github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -117,6 +130,7 @@ github.com/stretchr/objx v0.4.0/go.mod h1:YvHI0jy2hoMjB+UWwv71VJQ9isScKT/TqJzVSS
github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo= github.com/stretchr/objx v0.5.0/go.mod h1:Yh+to48EsGEfYuaHDzXPcE3xhTkx73EhmCGUpEOglKo=
github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY= github.com/stretchr/objx v0.5.2 h1:xuMeJ0Sdp5ZMRXx/aWO6RZxdr3beISkG5/G/aIRr3pY=
github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA= github.com/stretchr/objx v0.5.2/go.mod h1:FRsXN1f5AsAjCGJKqEizvkpNtU+EGNCLh3NxZ/8L+MA=
github.com/stretchr/testify v1.3.0/go.mod h1:M5WIy9Dh21IEIfnGCwXGc5bZfKNJtfHm1UVUgZn+9EI=
github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg= 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.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.0/go.mod h1:yNjHg4UonilssWZ8iaSj1OCr/vHnekPRkoO+kdMU+MU=

View file

@ -0,0 +1,29 @@
package config
import "fmt"
type AppConfig struct {
ListenIP string `json:"listen_ip" yaml:"listen_ip" toml:"listen_ip" validate:"required,ip"`
ListenPort int `json:"listen_port" yaml:"listen_port" toml:"listen_port" validate:"required"`
}
func (c *AppConfig) GenerateIP() string {
return fmt.Sprintf("%s:%d", c.ListenIP, c.ListenPort)
}
func NewAppConfig(port *int, ip *string) AppConfig {
defaultPort := 8080
defaultIp := "127.0.0.1"
if port == nil {
port = &defaultPort
}
if ip == nil {
ip = &defaultIp
}
return AppConfig{
ListenIP: *ip,
ListenPort: *port,
}
}

View file

@ -1,29 +1,22 @@
package config package config
import "fmt" type Config struct {
App AppConfig `json:"app" yaml:"app" toml:"app" validate:"required"`
type AppConfig struct { DB DatabaseConfig `json:"database" yaml:"database" toml:"database" validate:"required"`
ListenIP string `json:"listen_ip"`
ListenPort int `json:"listen_port"`
} }
func (c *AppConfig) GenerateIP() string { // NewConfig will create a new Config struct with the given AppConfig and DatabaseConfig.
return fmt.Sprintf("%s:%d", c.ListenIP, c.ListenPort) func NewConfig(app AppConfig, db DatabaseConfig) Config {
} return Config{
App: app,
func NewAppConfig(port *int, ip *string) AppConfig { DB: db,
defaultPort := 8080 }
defaultIp := "127.0.0.1" }
if port == nil { // NewDefaultConfig will create a new Config struct with default values.
port = &defaultPort func NewDefaultConfig() Config {
} return Config{
if ip == nil { App: NewAppConfig(nil, nil),
ip = &defaultIp DB: NewDatabaseConfig(nil, nil, nil, nil, nil, nil),
}
return AppConfig{
ListenIP: *ip,
ListenPort: *port,
} }
} }

View file

@ -0,0 +1,99 @@
package config
import "fmt"
type DriverType string
const (
MySQL DriverType = "mysql"
Postgres DriverType = "postgres"
SQLite DriverType = "sqlite"
)
// DatabaseConfig will hold the database configuration.
//
// If the driver is SQLite, the Host field should be the path to the database file.
// Other configurations options will be ignored.
type DatabaseConfig struct {
Driver DriverType `json:"driver" yaml:"driver" toml:"driver" validate:"required"`
Host string `json:"host" yaml:"host" toml:"host" validate:"required"`
Port int `json:"port" yaml:"port" toml:"port"`
User string `json:"user" yaml:"user" toml:"user"`
Pass string `json:"pass" yaml:"pass" toml:"pass"`
DBName string `json:"db_name" yaml:"db_name" toml:"db_name"`
}
func NewDatabaseConfig(driver *DriverType, host, user, pass, dbName *string, port *int) DatabaseConfig {
defaultDriver := SQLite
defaultHost := "./database.db"
defaultPort := 0
defaultUser := ""
defaultPass := ""
defaultDBName := ""
if driver == nil {
driver = &defaultDriver
}
if host == nil {
host = &defaultHost
}
if port == nil {
port = &defaultPort
}
if user == nil {
user = &defaultUser
}
if pass == nil {
pass = &defaultPass
}
if dbName == nil {
dbName = &defaultDBName
}
return DatabaseConfig{
Driver: *driver,
Host: *host,
Port: *port,
User: *user,
Pass: *pass,
DBName: *dbName,
}
}
// GenerateDSN generates the Data Source Name (DSN) for the database connection.
func (c *DatabaseConfig) GenerateDSN() string {
switch c.Driver {
case MySQL:
return c.generateMySQLDSN()
case Postgres:
return c.generatePostgresDSN()
case SQLite:
return c.generateSQLiteDSN()
default:
return ""
}
}
func (c *DatabaseConfig) GetDriver() string {
switch c.Driver {
case MySQL:
return "mysql"
case Postgres:
return "pgx"
case SQLite:
return "sqlite3"
default:
return ""
}
return string(c.Driver)
}
func (c *DatabaseConfig) generateSQLiteDSN() string {
return c.Host
}
func (c *DatabaseConfig) generateMySQLDSN() string {
return fmt.Sprintf("%s:%s@tcp(%s:%d)/%s", c.User, c.Pass, c.Host, c.Port, c.DBName)
}
func (c *DatabaseConfig) generatePostgresDSN() string {
return fmt.Sprintf("postgres://%s:%s@%s:%d/%s", c.User, c.Pass, c.Host, c.Port, c.DBName)
}

106
internal/config/parse.go Normal file
View file

@ -0,0 +1,106 @@
package config
import (
"encoding/json"
"fmt"
"github.com/go-playground/validator/v10"
"io"
"os"
"path/filepath"
"github.com/pelletier/go-toml/v2"
"gopkg.in/yaml.v3"
)
type FileType string
const (
YAML FileType = "yaml"
TOML FileType = "toml"
JSON FileType = "json"
)
// getFileType returns the file type based on the file extension.
func getFileType(path string) (FileType, error) {
// Get the file extension
ext := filepath.Ext(path)
switch ext {
case ".yaml", ".yml":
return YAML, nil
case ".toml":
return TOML, nil
case ".json":
return JSON, nil
default:
return "", fmt.Errorf("unsupported file type: %s", ext)
}
}
func ParseConfig(path string) (Config, error) {
fileType, err := getFileType(path)
if err != nil {
return Config{}, err
}
file, err := os.Open(path)
if err != nil {
return Config{}, err
}
defer file.Close()
data, err := io.ReadAll(file)
var config Config
switch fileType {
case YAML:
config, err = parseYAML(data)
case TOML:
config, err = parseTOML(data)
case JSON:
config, err = parseJSON(data)
default:
return Config{}, fmt.Errorf("unsupported file type: %s", fileType)
}
if err != nil {
return Config{}, err
}
validate := validator.New(validator.WithRequiredStructEnabled())
err = validate.Struct(config)
if err != nil {
return Config{}, err
}
return config, nil
}
func parseTOML(data []byte) (Config, error) {
var appConfig Config
err := toml.Unmarshal(data, &appConfig)
if err != nil {
return Config{}, err
}
return appConfig, nil
}
func parseYAML(data []byte) (Config, error) {
var appConfig Config
err := yaml.Unmarshal(data, &appConfig)
if err != nil {
return Config{}, err
}
return appConfig, nil
}
func parseJSON(data []byte) (Config, error) {
var appConfig Config
err := json.Unmarshal(data, &appConfig)
if err != nil {
return Config{}, err
}
return appConfig, nil
}