package main import ( "fmt" "github.com/BurntSushi/toml" ) // DatabaseDriver represents the database driver specified in the config file, // mirroring the legacy DatabaseDriver(StrEnum). It is a numeric enum simulated // with const + iota; its string form is produced by databaseDriverFromString. type DatabaseDriver int const ( // DatabaseDriverSqlite means the SQLite database is used. DatabaseDriverSqlite DatabaseDriver = iota // DatabaseDriverMysql means the MySQL database is used. DatabaseDriverMysql ) // databaseDriverFromString converts a driver string from the config file into a // DatabaseDriver. An unrecognized string returns an error. func databaseDriverFromString(s string) (DatabaseDriver, error) { switch s { case "sqlite": return DatabaseDriverSqlite, nil case "mysql": return DatabaseDriverMysql, nil default: return 0, fmt.Errorf("invalid database driver: %q", s) } } // SqliteDatabaseConfig is the connection config for SQLite, mirroring the // legacy SqliteDatabaseConfig. type SqliteDatabaseConfig struct { // Path is the database file path. Path string `toml:"path"` } // MysqlDatabaseConfig is the connection config for MySQL, mirroring the legacy // MysqlDatabaseConfig. type MysqlDatabaseConfig struct { // Host is the database host. Host string `toml:"host"` // Port is the database port. Port int `toml:"port"` // User is the database user. User string `toml:"user"` // Password is the database password. Password string `toml:"password"` // Database is the database name. Database string `toml:"database"` } // DatabaseConfig describes the database driver and its concrete config, mirroring // the legacy DatabaseConfig. Driver is the discriminator deciding whether Sqlite // or Mysql is populated. type DatabaseConfig struct { // Driver is the selected database driver. Driver DatabaseDriver // Sqlite is non-nil when Driver is DatabaseDriverSqlite. Sqlite *SqliteDatabaseConfig // Mysql is non-nil when Driver is DatabaseDriverMysql. Mysql *MysqlDatabaseConfig } // WebConfig is the web server config, mirroring the legacy WebConfig. type WebConfig struct { // Port is the web server listening port. Port int `toml:"port"` } // OthersConfig holds miscellaneous options, mirroring the legacy OthersConfig. type OthersConfig struct { // Debug indicates whether debug mode is enabled. Debug bool `toml:"debug"` // AutoTokenCleanDuration is the interval (in seconds) for auto-cleaning // outdated tokens. AutoTokenCleanDuration int `toml:"auto-token-clean-duration"` } // Config is the full configuration parsed from the config file, mirroring the // legacy Config. type Config struct { // Database is the database-related config. Database DatabaseConfig // Web is the web-server-related config. Web WebConfig // Others is the miscellaneous config. Others OthersConfig } // rawDatabaseConfig is an internal struct used only for TOML decoding. Its // Config field is stored as a toml.Primitive so it can be dispatched and decoded // in two phases based on Driver. type rawDatabaseConfig struct { Driver string `toml:"driver"` Config toml.Primitive `toml:"config"` } // rawConfig is an internal struct used only for TOML decoding, in tandem with // the two-phase decode. type rawConfig struct { Database rawDatabaseConfig `toml:"database"` Web WebConfig `toml:"web"` Others OthersConfig `toml:"others"` } // LoadConfig reads and parses the TOML config file at the given path, returning // the parsed config or an error. It merges the legacy setup_config and // get_config: the first phase uses toml.DecodeFile to defer database.config, and // the second phase dispatches PrimitiveDecode into SqliteDatabaseConfig or // MysqlDatabaseConfig based on the driver. func LoadConfig(path string) (*Config, error) { var raw rawConfig meta, err := toml.DecodeFile(path, &raw) if err != nil { return nil, err } driver, err := databaseDriverFromString(raw.Database.Driver) if err != nil { return nil, err } cfg := &Config{ Web: raw.Web, Others: raw.Others, Database: DatabaseConfig{ Driver: driver, }, } switch driver { case DatabaseDriverSqlite: var s SqliteDatabaseConfig if err := meta.PrimitiveDecode(raw.Database.Config, &s); err != nil { return nil, err } cfg.Database.Sqlite = &s case DatabaseDriverMysql: var m MysqlDatabaseConfig if err := meta.PrimitiveDecode(raw.Database.Config, &m); err != nil { return nil, err } cfg.Database.Mysql = &m } return cfg, nil }