summaryrefslogtreecommitdiff
path: root/gitconfig_parser.go
blob: edab556c5a46974791f3988229ad0501c7185ae0 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
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": "[email protected]",
//	    },
//	    "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)
		}
	}
}
*/