1 package driver
2
3 import (
4 "encoding/json"
5 "fmt"
6 "io/ioutil"
7 "net/url"
8 "os"
9 "path/filepath"
10 )
11
12
13 type settings struct {
14
15 Configs []namedConfig `json:"configs"`
16 }
17
18
19 type namedConfig struct {
20 Name string `json:"name"`
21 config
22 }
23
24
25 func settingsFileName() (string, error) {
26
27 dir, err := os.UserConfigDir()
28 if err != nil {
29 return "", err
30 }
31 return filepath.Join(dir, "pprof", "settings.json"), nil
32 }
33
34
35 func readSettings(fname string) (*settings, error) {
36 data, err := ioutil.ReadFile(fname)
37 if err != nil {
38 if os.IsNotExist(err) {
39 return &settings{}, nil
40 }
41 return nil, fmt.Errorf("could not read settings: %w", err)
42 }
43 settings := &settings{}
44 if err := json.Unmarshal(data, settings); err != nil {
45 return nil, fmt.Errorf("could not parse settings: %w", err)
46 }
47 for i := range settings.Configs {
48 settings.Configs[i].resetTransient()
49 }
50 return settings, nil
51 }
52
53
54 func writeSettings(fname string, settings *settings) error {
55 data, err := json.MarshalIndent(settings, "", " ")
56 if err != nil {
57 return fmt.Errorf("could not encode settings: %w", err)
58 }
59
60
61
62
63 if err := os.MkdirAll(filepath.Dir(fname), 0700); err != nil {
64 return fmt.Errorf("failed to create settings directory: %w", err)
65 }
66
67 if err := ioutil.WriteFile(fname, data, 0644); err != nil {
68 return fmt.Errorf("failed to write settings: %w", err)
69 }
70 return nil
71 }
72
73
74 type configMenuEntry struct {
75 Name string
76 URL string
77 Current bool
78 UserConfig bool
79 }
80
81
82 func configMenu(fname string, url url.URL) []configMenuEntry {
83
84 configs := []namedConfig{{Name: "Default", config: defaultConfig()}}
85 if settings, err := readSettings(fname); err == nil {
86
87 configs = append(configs, settings.Configs...)
88 }
89
90
91 result := make([]configMenuEntry, len(configs))
92 lastMatch := -1
93 for i, cfg := range configs {
94 dst, changed := cfg.config.makeURL(url)
95 if !changed {
96 lastMatch = i
97 }
98 result[i] = configMenuEntry{
99 Name: cfg.Name,
100 URL: dst.String(),
101 UserConfig: (i != 0),
102 }
103 }
104
105 if lastMatch >= 0 {
106 result[lastMatch].Current = true
107 }
108 return result
109 }
110
111
112 func editSettings(fname string, fn func(s *settings) error) error {
113 settings, err := readSettings(fname)
114 if err != nil {
115 return err
116 }
117 if err := fn(settings); err != nil {
118 return err
119 }
120 return writeSettings(fname, settings)
121 }
122
123
124 func setConfig(fname string, request url.URL) error {
125 q := request.Query()
126 name := q.Get("config")
127 if name == "" {
128 return fmt.Errorf("invalid config name")
129 }
130 cfg := currentConfig()
131 if err := cfg.applyURL(q); err != nil {
132 return err
133 }
134 return editSettings(fname, func(s *settings) error {
135 for i, c := range s.Configs {
136 if c.Name == name {
137 s.Configs[i].config = cfg
138 return nil
139 }
140 }
141 s.Configs = append(s.Configs, namedConfig{Name: name, config: cfg})
142 return nil
143 })
144 }
145
146
147 func removeConfig(fname, config string) error {
148 return editSettings(fname, func(s *settings) error {
149 for i, c := range s.Configs {
150 if c.Name == config {
151 s.Configs = append(s.Configs[:i], s.Configs[i+1:]...)
152 return nil
153 }
154 }
155 return fmt.Errorf("config %s not found", config)
156 })
157 }
158
View as plain text