diff options
author | Simon Garrelou <simon.garrelou@airbus.com> | 2023-12-13 13:27:59 +0100 |
---|---|---|
committer | Simon Garrelou <simon.garrelou@airbus.com> | 2023-12-13 13:27:59 +0100 |
commit | 9e6a87b8c756ef12856970ecd39c2c2246df5a56 (patch) | |
tree | a5ed398e713f783c1e334b4e95774c911d07734a | |
parent | 0f31dab7f24fc0cc97f4874010cc35dde9f3a756 (diff) | |
download | nix-tools-9e6a87b8c756ef12856970ecd39c2c2246df5a56.tar.gz nix-tools-9e6a87b8c756ef12856970ecd39c2c2246df5a56.zip |
add hm-search (home-manager)
-rw-r--r-- | cmd/hm-search/main.go | 51 | ||||
-rw-r--r-- | cmd/hm-search/options.go | 137 | ||||
-rw-r--r-- | nix/drv.go | 24 |
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 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "flag" | ||
5 | "fmt" | ||
6 | "log" | ||
7 | "strings" | ||
8 | |||
9 | colors "git.sixfoisneuf.fr/nix-tools" | ||
10 | ) | ||
11 | |||
12 | var ( | ||
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 | |||
18 | func 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 @@ | |||
1 | package main | ||
2 | |||
3 | import ( | ||
4 | "encoding/json" | ||
5 | "fmt" | ||
6 | "os" | ||
7 | "strings" | ||
8 | |||
9 | nix "git.sixfoisneuf.fr/nix-tools/nix" | ||
10 | ) | ||
11 | |||
12 | type 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 | |||
22 | type Example struct { | ||
23 | Type string | ||
24 | Text string | ||
25 | } | ||
26 | |||
27 | type DefaultValue struct { | ||
28 | Type string | ||
29 | Text string | ||
30 | } | ||
31 | |||
32 | var optionsExpr = ` | ||
33 | let | ||
34 | hm = import <home-manager> {}; | ||
35 | in | ||
36 | hm.docs.json | ||
37 | ` | ||
38 | |||
39 | func 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 | |||
99 | func (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 | |||
118 | func (d DefaultValue) String() string { | ||
119 | return fmt.Sprintf("(default: %s)", d.Text) | ||
120 | } | ||
121 | |||
122 | func (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 @@ | |||
1 | package nix | ||
2 | |||
3 | import ( | ||
4 | "bytes" | ||
5 | "fmt" | ||
6 | "os/exec" | ||
7 | ) | ||
8 | |||
9 | func 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 | } | ||