forge/gitconfig_parser.go

144 lines
3.9 KiB
Go

package main
// playing with gemini to do simple tasks. it kicked this out
import (
"bufio"
"errors"
"fmt"
"os"
"path/filepath"
"strings"
)
// ParsedGitConfig represents the structure of a .gitconfig file,
// which is a map of section names to a map of their key-value pairs.
// For example:
//
// {
// "user": {
// "name": "John Doe",
// "email": "john.doe@example.com",
// },
// "core": {
// "editor": "vim",
// },
// }
type ParsedGitConfig map[string]map[string]string
// ParseGlobalGitConfig finds and parses the global .gitconfig file for the current user.
// It is platform-agnostic and works on Windows, macOS, and Linux.
// It returns the parsed configuration or an error if the file cannot be found or read.
func ParseGlobalGitConfig() (ParsedGitConfig, error) {
// os.UserHomeDir() is the platform-agnostic way to get the user's home directory.
homeDir, err := os.UserHomeDir()
if err != nil {
return nil, fmt.Errorf("could not get user home directory: %w", err)
}
// filepath.Join correctly constructs the path for the current OS.
gitConfigPath := filepath.Join(homeDir, ".gitconfig")
file, err := os.Open(gitConfigPath)
if err != nil {
if errors.Is(err, os.ErrNotExist) {
return nil, fmt.Errorf(".gitconfig file not found at %s", gitConfigPath)
}
return nil, fmt.Errorf("could not open .gitconfig file: %w", err)
}
defer file.Close()
config := make(ParsedGitConfig)
var currentSection string
scanner := bufio.NewScanner(file)
for scanner.Scan() {
line := strings.TrimSpace(scanner.Text())
// Ignore empty lines and comments
if line == "" || line[0] == '#' || line[0] == ';' {
continue
}
// Check for a new section
if strings.HasPrefix(line, "[") && strings.HasSuffix(line, "]") {
sectionName := line[1 : len(line)-1]
// Handle subsections like [remote "origin"] by splitting them.
// For simplicity, we'll just use the full string as the key.
// A more complex parser might create nested maps.
currentSection = strings.TrimSpace(sectionName)
if _, exists := config[currentSection]; !exists {
config[currentSection] = make(map[string]string)
}
continue
}
// Parse key-value pairs within a section
if currentSection != "" {
// Split by "=". Use SplitN to handle values that might contain "=".
parts := strings.SplitN(line, "=", 2)
if len(parts) == 2 {
key := strings.TrimSpace(parts[0])
value := strings.TrimSpace(parts[1])
config[currentSection][key] = value
}
}
}
if err := scanner.Err(); err != nil {
return nil, fmt.Errorf("error reading .gitconfig file: %w", err)
}
return config, nil
}
// GetValue retrieves a specific value from the parsed git config.
// It takes the section and key as input (e.g., "user", "name").
// It returns the value and a boolean indicating if the key was found.
func (c ParsedGitConfig) GetValue(section, key string) (string, bool) {
if sectionMap, ok := c[section]; ok {
if value, ok := sectionMap[key]; ok {
return value, true
}
}
return "", false
}
/*
// main function to demonstrate the usage of ParseGlobalGitConfig.
func main() {
config, err := ParseGlobalGitConfig()
if err != nil {
fmt.Fprintf(os.Stderr, "Error: %v\n", err)
os.Exit(1)
}
fmt.Println("Successfully parsed global .gitconfig file.")
fmt.Println("-----------------------------------------")
// Example of using GetValue to retrieve the user's name and email.
userName, found := config.GetValue("user", "name")
if found {
fmt.Printf("User Name: %s\n", userName)
} else {
fmt.Println("User name not found.")
}
userEmail, found := config.GetValue("user", "email")
if found {
fmt.Printf("User Email: %s\n", userEmail)
} else {
fmt.Println("User email not found.")
}
fmt.Println("\nFull configuration:")
// Print out the full parsed configuration
for section, keys := range config {
fmt.Printf("[%s]\n", section)
for key, value := range keys {
fmt.Printf(" %s = %s\n", key, value)
}
}
}
*/