aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSimon Garrelou <simon.garrelou@airbus.com>2023-12-13 13:27:59 +0100
committerSimon Garrelou <simon.garrelou@airbus.com>2023-12-13 13:27:59 +0100
commit9e6a87b8c756ef12856970ecd39c2c2246df5a56 (patch)
treea5ed398e713f783c1e334b4e95774c911d07734a
parent0f31dab7f24fc0cc97f4874010cc35dde9f3a756 (diff)
downloadnix-tools-9e6a87b8c756ef12856970ecd39c2c2246df5a56.tar.gz
nix-tools-9e6a87b8c756ef12856970ecd39c2c2246df5a56.zip
add hm-search (home-manager)
-rw-r--r--cmd/hm-search/main.go51
-rw-r--r--cmd/hm-search/options.go137
-rw-r--r--nix/drv.go24
3 files changed, 212 insertions, 0 deletions
diff --git a/cmd/hm-search/main.go b/cmd/hm-search/main.go
new file mode 100644
index 0000000..e2a43dc
--- /dev/null
+++ b/cmd/hm-search/main.go
@@ -0,0 +1,51 @@
1package main
2
3import (
4 "flag"
5 "fmt"
6 "log"
7 "strings"
8
9 colors "git.sixfoisneuf.fr/nix-tools"
10)
11
12var (
13 source = flag.String("source", "<home-manager>", "Path to the home-manager folder")
14 examples = flag.Bool("examples", false, "Display examples")
15 desc = flag.Bool("desc", false, "Also search in module description")
16)
17
18func main() {
19 flag.Parse()
20
21 query := flag.Arg(0)
22
23 options, err := LoadOptions(*source)
24 if err != nil {
25 log.Fatalf("error loading options: %v", err)
26 }
27
28 for _, o := range options {
29 if !strings.Contains(strings.ToLower(o.Name), strings.ToLower(query)) {
30 if !*desc {
31 continue
32 }
33
34 if !strings.Contains(strings.ToLower(o.Description), strings.ToLower(query)) {
35 continue
36 }
37 }
38
39 colors.Red.Printf("%s", o.Name)
40 if o.Default.Type != "" {
41 fmt.Printf(" %s", o.Default)
42 }
43 fmt.Printf("\n%s", o.RenderDescription())
44
45 if o.Example.Type != "" && *examples {
46 fmt.Println(o.Example)
47 }
48
49 fmt.Println()
50 }
51}
diff --git a/cmd/hm-search/options.go b/cmd/hm-search/options.go
new file mode 100644
index 0000000..accd2af
--- /dev/null
+++ b/cmd/hm-search/options.go
@@ -0,0 +1,137 @@
1package main
2
3import (
4 "encoding/json"
5 "fmt"
6 "os"
7 "strings"
8
9 nix "git.sixfoisneuf.fr/nix-tools/nix"
10)
11
12type Option struct {
13 Name string
14 Declaration []string
15 Description string
16 ReadOnly bool
17 Type string
18 Default DefaultValue
19 Example Example
20}
21
22type Example struct {
23 Type string
24 Text string
25}
26
27type DefaultValue struct {
28 Type string
29 Text string
30}
31
32var optionsExpr = `
33let
34 hm = import <home-manager> {};
35in
36hm.docs.json
37`
38
39func LoadOptions(nixpkgs string) ([]Option, error) {
40 pkgs, err := nix.LoadNixpkgs(nixpkgs)
41 if err != nil {
42 return nil, fmt.Errorf("loading '%s': %v", nixpkgs, err)
43 }
44
45 drv, err := pkgs.EvalString(optionsExpr)
46 if err != nil {
47 return nil, fmt.Errorf("running Nix query: %v", err)
48 }
49
50 path, err := nix.BuildDerivation(drv)
51 if err != nil {
52 return nil, fmt.Errorf("building options.json: %v", err)
53 }
54
55 path = strings.TrimSpace(path)
56 path = path + "/share/doc/home-manager/options.json"
57
58 var res map[string]map[string]interface{}
59 data, err := os.ReadFile(path)
60 if err != nil {
61 return nil, fmt.Errorf("reading %s: %v", path, err)
62 }
63
64 err = json.Unmarshal(data, &res)
65 if err != nil {
66 return nil, fmt.Errorf("parsing JSON in %s: %v", path, err)
67 }
68
69 var options []Option
70 for option, params := range res {
71 o := Option{
72 Name: option,
73 }
74
75 if desc, ok := params["description"]; ok {
76 if desc, ok := desc.(string); ok {
77 o.Description = desc
78 }
79 }
80
81 if expl, ok := params["example"]; ok {
82 expl := expl.(map[string]interface{})
83 o.Example.Type = expl["_type"].(string)
84 o.Example.Text = expl["text"].(string)
85 }
86
87 if def, ok := params["default"]; ok {
88 def := def.(map[string]interface{})
89 o.Default.Type = def["_type"].(string)
90 o.Default.Text = def["text"].(string)
91 }
92
93 options = append(options, o)
94 }
95
96 return options, nil
97}
98
99func (e Example) String() string {
100 sb := strings.Builder{}
101
102 sb.WriteString(" | [Example]\n")
103 sb.WriteString(" | Type: ")
104 sb.WriteString(e.Type)
105 sb.WriteString("\n")
106 sb.WriteString(" | \n")
107
108 parts := strings.Split(e.Text, "\n")
109 for _, p := range parts {
110 sb.WriteString(" | ")
111 sb.WriteString(p)
112 sb.WriteString("\n")
113 }
114
115 return sb.String()
116}
117
118func (d DefaultValue) String() string {
119 return fmt.Sprintf("(default: %s)", d.Text)
120}
121
122func (o Option) RenderDescription() string {
123 sb := strings.Builder{}
124
125 o.Description = strings.Trim(o.Description, "\n")
126
127 parts := strings.Split(o.Description, "\n")
128 for _, p := range parts {
129 p = strings.TrimSpace(p)
130 sb.WriteString(" ")
131 sb.WriteString(p)
132
133 sb.WriteString("\n")
134 }
135
136 return sb.String()
137}
diff --git a/nix/drv.go b/nix/drv.go
new file mode 100644
index 0000000..3d92dab
--- /dev/null
+++ b/nix/drv.go
@@ -0,0 +1,24 @@
1package nix
2
3import (
4 "bytes"
5 "fmt"
6 "os/exec"
7)
8
9func BuildDerivation(path string) (string, error) {
10 cmd := exec.Command("nix-store", "-r", path)
11
12 stdout := bytes.Buffer{}
13 stderr := bytes.Buffer{}
14
15 cmd.Stdout = &stdout
16 cmd.Stderr = &stderr
17
18 err := cmd.Run()
19 if err != nil {
20 return "", fmt.Errorf("realising '%s': %v\n\"\"\"\n%s\n\"\"\"", path, err, string(stderr.Bytes()))
21 }
22
23 return string(stdout.Bytes()), nil
24}