aboutsummaryrefslogtreecommitdiff
path: root/utils
diff options
context:
space:
mode:
authorBobby <[email protected]>2026-03-06 22:15:09 +0530
committerBobby <[email protected]>2026-03-06 22:15:09 +0530
commit3f07a4b6c745707f135a7a97e93b0fa770b67873 (patch)
tree2a16507183d139da1338a61fc4f222d094fff138 /utils
parent7e32d634cd488b0f100b62ad29234951dc6fb6e3 (diff)
downloaddove-3f07a4b6c745707f135a7a97e93b0fa770b67873.tar.xz
dove-3f07a4b6c745707f135a7a97e93b0fa770b67873.zip
Add configuration and logging modules with TOML support
- Implement configuration management in the config package - Define constants and types for server and mailbox configurations - Create functions for loading and parsing configuration files - Introduce logging functionality with customizable log levels and formats - Add error handling and messages for configuration and logging operations - Include TOML utilities for default value application and content marshaling
Diffstat (limited to 'utils')
-rw-r--r--utils/collections/types.go3
-rw-r--r--utils/errors/errors.go14
-rw-r--r--utils/logger/constants.go25
-rw-r--r--utils/logger/functions.go53
-rw-r--r--utils/logger/logger.go83
-rw-r--r--utils/logger/types.go11
-rw-r--r--utils/toml/defaults.go64
-rw-r--r--utils/toml/load.go21
-rw-r--r--utils/toml/parse.go40
-rw-r--r--utils/toml/unmarshal.go11
10 files changed, 325 insertions, 0 deletions
diff --git a/utils/collections/types.go b/utils/collections/types.go
new file mode 100644
index 0000000..c75d1ea
--- /dev/null
+++ b/utils/collections/types.go
@@ -0,0 +1,3 @@
+package collections
+
+type Record map[string]any
diff --git a/utils/errors/errors.go b/utils/errors/errors.go
new file mode 100644
index 0000000..015356a
--- /dev/null
+++ b/utils/errors/errors.go
@@ -0,0 +1,14 @@
+package errors
+
+import (
+ "errors"
+ "fmt"
+)
+
+func Error(message string, arguments ...any) error {
+ if len(arguments) == 0 {
+ return errors.New(message)
+ }
+
+ return fmt.Errorf(message, arguments...)
+}
diff --git a/utils/logger/constants.go b/utils/logger/constants.go
new file mode 100644
index 0000000..aba37ae
--- /dev/null
+++ b/utils/logger/constants.go
@@ -0,0 +1,25 @@
+package logger
+
+const (
+ ANSI_RESET = "\033[0m"
+)
+
+const (
+ LEVEL_COLOR_DEBUG = "\033[35mDEBUG \033[0m"
+ LEVEL_COLOR_ERROR = "\033[31mERROR \033[0m"
+ LEVEL_COLOR_INFO = "\033[34mINFO \033[0m"
+ LEVEL_COLOR_WARN = "\033[33mWARN \033[0m"
+)
+
+const (
+ MESSAGE_COLOR_DEBUG = "\033[90m"
+ MESSAGE_COLOR_ERROR = "\033[31m"
+ MESSAGE_COLOR_INFO = "\033[97m"
+ MESSAGE_COLOR_SUCCESS = "\033[32m"
+ MESSAGE_COLOR_WARN = "\033[33m"
+)
+
+const (
+ PREFIX_COLOR = "\033[36m"
+ PREFIX_WIDTH = 15
+)
diff --git a/utils/logger/functions.go b/utils/logger/functions.go
new file mode 100644
index 0000000..7bd9850
--- /dev/null
+++ b/utils/logger/functions.go
@@ -0,0 +1,53 @@
+package logger
+
+import (
+ "fmt"
+ "strings"
+
+ "go.uber.org/zap/zapcore"
+)
+
+func formatLevel(level zapcore.Level, encoder zapcore.PrimitiveArrayEncoder) {
+ switch level {
+ case zapcore.DebugLevel:
+ encoder.AppendString(LEVEL_COLOR_DEBUG)
+ case zapcore.WarnLevel:
+ encoder.AppendString(LEVEL_COLOR_WARN)
+ case zapcore.ErrorLevel:
+ encoder.AppendString(LEVEL_COLOR_ERROR)
+ default:
+ encoder.AppendString(LEVEL_COLOR_INFO)
+ }
+}
+
+func formatPrefix(prefix string) string {
+ if prefix == "" {
+ return ""
+ }
+
+ padding := ""
+ if len(prefix) < PREFIX_WIDTH {
+ padding = strings.Repeat(" ", PREFIX_WIDTH-len(prefix))
+ }
+
+ return PREFIX_COLOR + "[" + prefix + "]" + ANSI_RESET + padding
+}
+
+func colorizeMessage(level logLevel, message string) string {
+ switch level {
+ case levelDebug:
+ return MESSAGE_COLOR_DEBUG + message + ANSI_RESET
+ case levelWarn:
+ return MESSAGE_COLOR_WARN + message + ANSI_RESET
+ case levelError:
+ return MESSAGE_COLOR_ERROR + message + ANSI_RESET
+ case levelSuccess:
+ return MESSAGE_COLOR_SUCCESS + message + ANSI_RESET
+ default:
+ return MESSAGE_COLOR_INFO + message + ANSI_RESET
+ }
+}
+
+func buildFullMessage(level logLevel, prefix string, message any) string {
+ return formatPrefix(prefix) + colorizeMessage(level, fmt.Sprint(message))
+}
diff --git a/utils/logger/logger.go b/utils/logger/logger.go
new file mode 100644
index 0000000..b1d1809
--- /dev/null
+++ b/utils/logger/logger.go
@@ -0,0 +1,83 @@
+package logger
+
+import (
+ "fmt"
+ "os"
+
+ "dove/messages"
+
+ "go.uber.org/zap"
+ "go.uber.org/zap/zapcore"
+)
+
+var (
+ instance *zap.Logger
+ atomicLevel zap.AtomicLevel
+)
+
+func Init() {
+ atomicLevel = zap.NewAtomicLevelAt(zapcore.InfoLevel)
+
+ encoderConfig := zapcore.EncoderConfig{
+ LevelKey: "level",
+ MessageKey: "msg",
+ LineEnding: "\n",
+ EncodeLevel: formatLevel,
+ }
+
+ encoder := zapcore.NewConsoleEncoder(encoderConfig)
+ stdoutSink := zapcore.AddSync(os.Stdout)
+ stderrSink := zapcore.AddSync(os.Stderr)
+
+ core := zapcore.NewTee(
+ zapcore.NewCore(encoder, stdoutSink, zap.LevelEnablerFunc(func(level zapcore.Level) bool {
+ return level < zapcore.WarnLevel && atomicLevel.Enabled(level)
+ })),
+ zapcore.NewCore(encoder, stderrSink, zap.LevelEnablerFunc(func(level zapcore.Level) bool {
+ return level >= zapcore.WarnLevel && atomicLevel.Enabled(level)
+ })),
+ )
+
+ instance = zap.New(core, zap.AddCaller())
+}
+
+func SetDebug(enabled bool) {
+ if enabled {
+ atomicLevel.SetLevel(zapcore.DebugLevel)
+ } else {
+ atomicLevel.SetLevel(zapcore.InfoLevel)
+ }
+}
+
+func Debugf(prefix string, format string, arguments ...any) {
+ emit(levelDebug, zapcore.DebugLevel, prefix, fmt.Sprintf(format, arguments...))
+}
+
+func Infof(prefix string, format string, arguments ...any) {
+ emit(levelInfo, zapcore.InfoLevel, prefix, fmt.Sprintf(format, arguments...))
+}
+
+func Successf(prefix string, format string, arguments ...any) {
+ emit(levelSuccess, zapcore.InfoLevel, prefix, fmt.Sprintf(format, arguments...))
+}
+
+func Warnf(prefix string, format string, arguments ...any) {
+ emit(levelWarn, zapcore.WarnLevel, prefix, fmt.Sprintf(format, arguments...))
+}
+
+func Errorf(prefix string, format string, arguments ...any) {
+ emit(levelError, zapcore.ErrorLevel, prefix, fmt.Sprintf(format, arguments...))
+}
+
+func Fatalf(prefix string, format string, arguments ...any) {
+ emit(levelError, zapcore.ErrorLevel, prefix, fmt.Sprintf(format, arguments...))
+ os.Exit(1)
+}
+
+func emit(levelLabel logLevel, zapLevel zapcore.Level, prefix string, message any) {
+ if instance == nil {
+ panic(messages.LoggerNotInitialized)
+ }
+
+ instance.Log(zapLevel, buildFullMessage(levelLabel, prefix, message))
+}
diff --git a/utils/logger/types.go b/utils/logger/types.go
new file mode 100644
index 0000000..fce392d
--- /dev/null
+++ b/utils/logger/types.go
@@ -0,0 +1,11 @@
+package logger
+
+type logLevel string
+
+const (
+ levelDebug logLevel = "debug"
+ levelInfo logLevel = "info"
+ levelWarn logLevel = "warn"
+ levelError logLevel = "error"
+ levelSuccess logLevel = "success"
+)
diff --git a/utils/toml/defaults.go b/utils/toml/defaults.go
new file mode 100644
index 0000000..e9ef032
--- /dev/null
+++ b/utils/toml/defaults.go
@@ -0,0 +1,64 @@
+package toml
+
+import (
+ "reflect"
+ "strconv"
+)
+
+func ApplyDefaults(target any) {
+ targetValue := reflect.ValueOf(target)
+
+ if targetValue.Kind() != reflect.Pointer || targetValue.Elem().Kind() != reflect.Struct {
+ return
+ }
+
+ applyStructDefaults(targetValue.Elem())
+}
+
+func applyStructDefaults(structValue reflect.Value) {
+ structType := structValue.Type()
+
+ for fieldIndex := range structType.NumField() {
+ fieldValue := structValue.Field(fieldIndex)
+ fieldDescriptor := structType.Field(fieldIndex)
+
+ if !fieldValue.CanSet() {
+ continue
+ }
+
+ if fieldValue.Kind() == reflect.Struct {
+ applyStructDefaults(fieldValue)
+ continue
+ }
+
+ defaultValue := fieldDescriptor.Tag.Get("default")
+ if defaultValue == "" {
+ continue
+ }
+
+ setDefaultValue(fieldValue, defaultValue)
+ }
+}
+
+func setDefaultValue(field reflect.Value, defaultValue string) {
+ if !isZeroValue(field) {
+ return
+ }
+
+ switch field.Kind() {
+ case reflect.String:
+ field.SetString(defaultValue)
+ case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
+ if parsed, parseError := strconv.ParseInt(defaultValue, 10, 64); parseError == nil {
+ field.SetInt(parsed)
+ }
+ case reflect.Bool:
+ if parsed, parseError := strconv.ParseBool(defaultValue); parseError == nil {
+ field.SetBool(parsed)
+ }
+ }
+}
+
+func isZeroValue(field reflect.Value) bool {
+ return field.IsZero()
+}
diff --git a/utils/toml/load.go b/utils/toml/load.go
new file mode 100644
index 0000000..a0cab4b
--- /dev/null
+++ b/utils/toml/load.go
@@ -0,0 +1,21 @@
+package toml
+
+import (
+ "os"
+
+ "dove/messages"
+ "dove/utils/collections"
+ "dove/utils/errors"
+)
+
+var loadedData collections.Record
+
+func LoadFile(filePath string) error {
+ fileContent, readError := os.ReadFile(filePath)
+ if readError != nil {
+ return errors.Error(messages.ConfigFileReadFailed, filePath, readError.Error())
+ }
+
+ loadedData = make(collections.Record)
+ return unmarshalContent(fileContent, &loadedData)
+}
diff --git a/utils/toml/parse.go b/utils/toml/parse.go
new file mode 100644
index 0000000..7e2e6f2
--- /dev/null
+++ b/utils/toml/parse.go
@@ -0,0 +1,40 @@
+package toml
+
+import (
+ "reflect"
+ "strings"
+
+ "dove/messages"
+ "dove/utils/errors"
+)
+
+func Parse(target any) error {
+ targetValue := reflect.ValueOf(target)
+ if targetValue.Kind() != reflect.Pointer || targetValue.Elem().Kind() != reflect.Struct {
+ return errors.Error(messages.ParseTargetMustBeStructPointer)
+ }
+
+ ApplyDefaults(target)
+
+ if loadedData == nil {
+ return nil
+ }
+
+ sectionName := resolveSectionName(targetValue)
+ sectionData, exists := loadedData[sectionName]
+ if !exists {
+ return nil
+ }
+
+ sectionBytes, marshalError := marshalSection(sectionData)
+ if marshalError != nil {
+ return errors.Error(messages.ConfigSectionInvalid, sectionName)
+ }
+
+ return unmarshalContent(sectionBytes, target)
+}
+
+func resolveSectionName(targetValue reflect.Value) string {
+ typeName := targetValue.Elem().Type().Name()
+ return strings.ToLower(typeName)
+}
diff --git a/utils/toml/unmarshal.go b/utils/toml/unmarshal.go
new file mode 100644
index 0000000..bc4b6f4
--- /dev/null
+++ b/utils/toml/unmarshal.go
@@ -0,0 +1,11 @@
+package toml
+
+import "github.com/pelletier/go-toml/v2"
+
+func unmarshalContent(data []byte, target any) error {
+ return toml.Unmarshal(data, target)
+}
+
+func marshalSection(sectionData any) ([]byte, error) {
+ return toml.Marshal(sectionData)
+}