package config import ( "encoding/json" "flag" "fmt" "os" "path/filepath" ) type Config struct { CouchDb CouchDbConfig `json:"couchDb"` MailSources []MailSource `json:"mailSources"` } type CouchDbConfig struct { URL string `json:"url"` User string `json:"user"` Password string `json:"password"` Database string `json:"database"` } type MailSource struct { Name string `json:"name"` Enabled bool `json:"enabled"` Protocol string `json:"protocol"` Host string `json:"host"` Port int `json:"port"` User string `json:"user"` Password string `json:"password"` Sync bool `json:"sync"` FolderFilter FolderFilter `json:"folderFilter"` MessageFilter MessageFilter `json:"messageFilter"` } type FolderFilter struct { Include []string `json:"include"` Exclude []string `json:"exclude"` } type MessageFilter struct { Since string `json:"since,omitempty"` // TODO: Add keyword filtering support // SubjectKeywords []string `json:"subjectKeywords,omitempty"` // Filter by keywords in subject // SenderKeywords []string `json:"senderKeywords,omitempty"` // Filter by keywords in sender addresses // RecipientKeywords []string `json:"recipientKeywords,omitempty"` // Filter by keywords in recipient addresses } func LoadConfig(path string) (*Config, error) { configFile, err := os.Open(path) if err != nil { return nil, err } defer configFile.Close() var config Config jsonParser := json.NewDecoder(configFile) if err = jsonParser.Decode(&config); err != nil { return nil, err } return &config, nil } // FindConfigFile searches for config.json in the following order: // 1. Path specified by -config flag // 2. ./config.json (current directory) // 3. ~/.config/mail2couch/config.json (user config directory) // 4. ~/.mail2couch.json (user home directory) func FindConfigFile() (string, error) { // Check for command line flag configFlag := flag.String("config", "", "Path to configuration file") flag.Parse() if *configFlag != "" { if _, err := os.Stat(*configFlag); err == nil { return *configFlag, nil } return "", fmt.Errorf("specified config file not found: %s", *configFlag) } // List of possible config file locations in order of preference candidates := []string{ "config.json", // Current directory "config/config.json", // Config subdirectory } // Add user directory paths if homeDir, err := os.UserHomeDir(); err == nil { candidates = append(candidates, filepath.Join(homeDir, ".config", "mail2couch", "config.json"), filepath.Join(homeDir, ".mail2couch.json"), ) } // Try each candidate location for _, candidate := range candidates { if _, err := os.Stat(candidate); err == nil { return candidate, nil } } return "", fmt.Errorf("no configuration file found. Searched locations: %v", candidates) } // LoadConfigWithDiscovery loads configuration using automatic file discovery func LoadConfigWithDiscovery() (*Config, error) { configPath, err := FindConfigFile() if err != nil { return nil, err } fmt.Printf("Using configuration file: %s\n", configPath) return LoadConfig(configPath) }