all repos

clerk @ 7b8e468

missing tooling for ledger/hledger

clerk/internal/settings/linter.go (view raw)

Oleksandr Smirnov Oleksandr Smirnov
olexsmir@gmail.com
feat: config (#6), 1 month ago
1
package settings
2
3
import (
4
	"encoding/json"
5
	"errors"
6
	"fmt"
7
	"maps"
8
	"strings"
9
10
	"olexsmir.xyz/clerk/internal/linter"
11
)
12
13
var lintRuleLookup = func() map[string]linter.RuleID {
14
	idx := make(map[string]linter.RuleID, 2*len(linter.Rules))
15
	for id := range linter.Rules {
16
		idx[string(id)] = id
17
		idx[normalizeKey(string(id))] = id
18
	}
19
	return idx
20
}()
21
22
func (s *Settings) setLint(v any) ([]string, error) {
23
	rules := make(map[linter.RuleID]linter.RuleConfig, len(s.Linter.Rules))
24
	maps.Copy(rules, s.Linter.Rules)
25
	s.Linter.Rules = rules
26
	return applyTable(v, func(name string, val any) ([]string, error) {
27
		id, ok := lintRuleLookup[name]
28
		if !ok {
29
			id, ok = lintRuleLookup[normalizeKey(name)]
30
		}
31
		if !ok {
32
			return []string{fmt.Sprintf("unknown lint rule %q", name)}, nil
33
		}
34
		rc, err := applyLintRule(s.Linter.Rules[id], val)
35
		if err != nil {
36
			return nil, err
37
		}
38
		s.Linter.Rules[id] = rc
39
		return nil, nil
40
	})
41
}
42
43
func applyLintRule(rc linter.RuleConfig, v any) (linter.RuleConfig, error) {
44
	switch v := v.(type) {
45
	case bool:
46
		if v {
47
			return rc, errors.New(`true is not supported (want false, "off", a severity, or an options table)`)
48
		}
49
		rc.Disabled = true
50
	case string:
51
		return applySeverity(rc, v)
52
	case map[string]any:
53
		opts := make(map[string]any, len(v))
54
		for k, val := range v {
55
			if normalizeKey(k) == "severity" {
56
				s, ok := val.(string)
57
				if !ok {
58
					return rc, fmt.Errorf("invalid severity %v (want string)", val)
59
				}
60
61
				var err error
62
				rc, err = applySeverity(rc, s)
63
				if err != nil {
64
					return rc, err
65
				}
66
				continue
67
			}
68
			opts[k] = val
69
		}
70
		if len(opts) > 0 {
71
			raw, err := json.Marshal(opts)
72
			if err != nil {
73
				return rc, err
74
			}
75
			rc.Options = raw
76
		}
77
	default:
78
		return rc, fmt.Errorf("invalid type %T (want bool, string, or table)", v)
79
	}
80
	return rc, nil
81
}
82
83
func applySeverity(rc linter.RuleConfig, s string) (linter.RuleConfig, error) {
84
	if strings.EqualFold(s, "off") {
85
		rc.Disabled = true
86
		return rc, nil
87
	}
88
	sev, ok := linter.ParseSeverity(s)
89
	if !ok {
90
		return rc, fmt.Errorf("invalid severity %q (want %q, %q, %q, %q, or %q)",
91
			s, "off", "error", "warn", "info", "hint")
92
	}
93
	rc.Disabled = false
94
	rc.Severity = sev
95
	return rc, nil
96
}