1 package main
2
3 import (
4 "fmt"
5 "github.com/go-git/go-git/v5"
6 "github.com/gookit/color"
7 "github.com/pelletier/go-toml/v2"
8 "os"
9 "os/user"
10 "strings"
11 )
12
13 // go-pest -- A reimplementation of cl-pest in Go, for the sake of learning a bit of Go
14 // See: https://git.spwbk.site/swatson/cl-pest/src/master/pest.lisp
15
16 type PestConfig struct {
17 Git GitConfig `toml:"git"`
18 Prompt PromptConfig `toml:"prompt"`
19 }
20
21 type GitConfig struct {
22 DisplayHead bool `toml:"display_head"`
23 DisplayBranch bool `toml:"display_branch"`
24 GitPrefix string `toml:"git_prefix"`
25 Colors Colors `toml:"colors"`
26 }
27
28 type PromptConfig struct {
29 DisplayUser bool `toml:"display_user"`
30 UserSuffix string `toml:"user_suffix"`
31 DisplayHostname bool `toml:"display_hostname"`
32 HostnameSuffix string `toml:"hostname_suffix"`
33 DisplayPwd bool `toml:"display_pwd"`
34 PwdSuffix string `toml:"pwd_suffix"`
35 PromptChar string `toml:"prompt_char"`
36 Colors Colors `toml:"colors"`
37 }
38
39 type Colors struct {
40 Fg []int `toml:"fg"`
41 Bg []int `toml:"bg"`
42 }
43
44 const tomlData = `
45 [git]
46 display_head = false
47 display_branch = false
48 git_prefix = ""
49 [git.colors]
50 fg = [0, 120, 50]
51 bg = [0, 0, 0]
52
53 [prompt]
54 display_user = false
55 user_suffix = ""
56 display_hostname = false
57 hostname_suffix = ""
58 display_pwd = true
59 pwd_suffix = ""
60 prompt_char = " λ "
61 [prompt.colors]
62 fg = [255, 255, 255]
63 bg = [0, 0, 0]
64 `
65
66 func parseTomlFromString(tomlString string) (PestConfig, error) {
67 var config PestConfig
68 err := toml.Unmarshal([]byte(tomlString), &config)
69 return config, err
70 }
71
72 func parseTomlFromFile(filePath string) (PestConfig, error) {
73 var cfg PestConfig
74 fileContent, err := os.ReadFile(filePath)
75 switch err {
76 case nil:
77 {
78 err = toml.Unmarshal(fileContent, &cfg)
79 return cfg, err
80 }
81 default:
82 {
83 // We had some kind of error, use the builtin config
84 cfg, err = parseTomlFromString(tomlData)
85 return cfg, err
86 }
87 }
88 }
89
90 func gitGetHead(filePath string) (string, string) {
91 gitObj, err := git.PlainOpen(filePath)
92 if err != nil {
93 // Likely not a git dir, return empty
94 return "", ""
95 }
96
97 headRef, err := gitObj.Head()
98 if err != nil {
99 panic(err)
100 }
101
102 sha := headRef.Hash().String()[:6]
103 branch := headRef.Name().Short()
104 return sha, branch
105 }
106
107 func getHostname() string {
108 hostname, err := os.Hostname()
109 if err != nil {
110 panic(err)
111 }
112 return hostname
113 }
114
115 func getUser() string {
116 username, err := user.Current()
117 if err != nil {
118 panic(err)
119 }
120 return username.Username
121 }
122
123 func getPwd() string {
124 pwd, err := os.Getwd()
125 if err != nil {
126 panic(err)
127 }
128 return pwd
129 }
130
131 type RGBColorInput struct {
132 r uint8
133 g uint8
134 b uint8
135 }
136
137 // make_style -- given a set of RGBColorInputs return a color.RGBColor pair
138 func make_style(fg, bg RGBColorInput) color.Style {
139 fg_s := color.RGB(fg.r, fg.g, fg.b)
140 bg_s := color.RGB(bg.r, bg.g, bg.b, true)
141 style := color.New(fg_s.Color(), bg_s.Color())
142 return style
143 }
144
145 func assemble_prompt(config *PestConfig) {
146 var prompt_builder strings.Builder
147 var git_prompt_builder strings.Builder
148
149 if config.Git.DisplayHead || config.Git.DisplayBranch {
150 sha, branch := gitGetHead(getPwd())
151 if config.Git.DisplayHead && config.Git.DisplayBranch {
152 git_prompt := sha + "|" + branch
153 git_prompt_builder.WriteString(config.Git.GitPrefix)
154 git_prompt_builder.WriteString(git_prompt)
155 } else if config.Git.DisplayHead {
156 git_prompt_builder.WriteString(config.Git.GitPrefix)
157 git_prompt_builder.WriteString(sha)
158 } else if config.Git.DisplayBranch {
159 git_prompt_builder.WriteString(config.Git.GitPrefix)
160 git_prompt_builder.WriteString(branch)
161 }
162 git_prompt_builder.WriteString(" ")
163 }
164
165 git_prompt_color := make_style(
166 RGBColorInput{
167 uint8(config.Git.Colors.Fg[0]),
168 uint8(config.Git.Colors.Fg[1]),
169 uint8(config.Git.Colors.Fg[2]),
170 },
171 RGBColorInput{
172 uint8(config.Git.Colors.Bg[0]),
173 uint8(config.Git.Colors.Bg[1]),
174 uint8(config.Git.Colors.Bg[2]),
175 })
176
177 if config.Prompt.DisplayUser {
178 prompt_builder.WriteString(getUser())
179 prompt_builder.WriteString(config.Prompt.UserSuffix)
180 }
181
182 if config.Prompt.DisplayHostname {
183 prompt_builder.WriteString(getHostname())
184 prompt_builder.WriteString(config.Prompt.HostnameSuffix)
185 }
186
187 if config.Prompt.DisplayPwd {
188 prompt_builder.WriteString(getPwd())
189 prompt_builder.WriteString(config.Prompt.PwdSuffix)
190 }
191
192 prompt_builder.WriteString(config.Prompt.PromptChar)
193
194 prompt_color := make_style(
195 RGBColorInput{
196 uint8(config.Prompt.Colors.Fg[0]),
197 uint8(config.Prompt.Colors.Fg[1]),
198 uint8(config.Prompt.Colors.Fg[2]), // This will of course panic if this number is > 255
199 },
200 RGBColorInput{
201 uint8(config.Prompt.Colors.Bg[0]),
202 uint8(config.Prompt.Colors.Bg[1]),
203 uint8(config.Prompt.Colors.Bg[2]),
204 })
205
206 git_prompt_color.Printf(git_prompt_builder.String())
207 prompt_color.Printf(prompt_builder.String())
208
209 }
210
211 func main() {
212
213 parsed_config, err := parseTomlFromFile("/home/swatson/Repos/go-pest/pest.cfg")
214 if err != nil {
215 fmt.Println("Couldn't parse TOML const!")
216 }
217
218 assemble_prompt(&parsed_config)
219
220 }