-
Notifications
You must be signed in to change notification settings - Fork 0
/
config.go
124 lines (113 loc) · 2.72 KB
/
config.go
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
package main
import (
"bufio"
"errors"
"fmt"
"io"
"os"
"path/filepath"
"strconv"
"strings"
)
type config struct {
Username username
Port int
// path to config directory.
path string
}
func getGlobalConfigPath() (string, error) {
home, err := os.UserHomeDir()
if err != nil {
return "", err
}
return filepath.Join(home, ".config", "shh"), nil
}
func getConfigPaths() (global string, project string, err error) {
global, err = getGlobalConfigPath()
if err != nil {
return "", "", fmt.Errorf("global: %w", err)
}
project, err = getProjectConfigPath()
if err != nil {
return "", "", fmt.Errorf("project: %w", err)
}
return global, project, nil
}
func getProjectConfigPath() (string, error) {
pth, err := findFileRecursive(".shhconfig")
if os.IsNotExist(err) {
// If we didn't find any project config, we're done
return "", nil
}
if err != nil {
return "", fmt.Errorf("find project config: %w", err)
}
return pth, nil
}
func configFromPaths(global, project string) (*config, error) {
globalConfPath := filepath.Join(global, "config")
fi, err := os.Open(globalConfPath)
if os.IsNotExist(err) {
return nil, errors.New("missing keys. run `shh gen-keys`")
}
if err != nil {
return nil, fmt.Errorf("open config: %w", err)
}
defer fi.Close()
globalConf, err := parseConfig(fi)
if err != nil {
return nil, fmt.Errorf("parse config: %w", err)
}
globalConf.path = global
// If there's no project-specific config, we're done
if project == "" {
return globalConf, nil
}
fi2, err := os.Open(project)
if err != nil {
return nil, fmt.Errorf("open project config: %w", err)
}
defer fi2.Close()
projectConf, err := parseConfig(fi2)
if err != nil {
return nil, fmt.Errorf("parse project config: %w", err)
}
// Override global config with project-specific settings. Only the
// username can be overridden now.
if projectConf.Username != "" {
globalConf.Username = projectConf.Username
}
return globalConf, nil
}
func parseConfig(r io.Reader) (*config, error) {
conf := &config{}
scn := bufio.NewScanner(r)
for i := 1; scn.Scan(); i++ {
line := scn.Text()
if line == "" {
continue
}
parts := strings.SplitN(line, "=", 2)
if len(parts) != 2 {
return nil, fmt.Errorf("invalid line %d: %s", i, line)
}
parts[0] = strings.TrimSpace(parts[0])
parts[1] = strings.TrimSpace(parts[1])
switch parts[0] {
case "username":
conf.Username = username(parts[1])
case "port":
var err error
conf.Port, err = strconv.Atoi(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid port %s: %w", parts[1], err)
}
default:
return nil, fmt.Errorf("unknown part %s", parts[0])
}
}
if err := scn.Err(); err != nil {
return nil, fmt.Errorf("scan: %w", err)
}
return conf, nil
}