1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
package main
import (
"context"
"encoding/json"
"flag"
"fmt"
"io"
"log/slog"
"maps"
"net"
"net/http"
"os"
"slices"
"golang.org/x/term"
"push-f.com/lex-surf/internal/lex"
"push-f.com/lex-surf/lex-fetch/at"
"push-f.com/lex-surf/lex-fetch/de"
"push-f.com/lex-surf/lex-fetch/progress"
"push-f.com/lex-surf/lex-fetch/uk"
)
type Fetcher interface {
Fetch(log *slog.Logger, client *http.Client, reporter *progress.Reporter) ([]lex.Law, error)
}
var fetchers = map[string]Fetcher{
"at": &at.Fetcher{},
"de": &de.Fetcher{},
"uk": &uk.Fetcher{},
}
var logger *slog.Logger
func printUsage() {
fmt.Printf("usage: %s [options] <country> <out>\n", os.Args[0])
fmt.Printf("where <country> is one of %v\n", slices.Sorted(maps.Keys(fetchers)))
fmt.Println("options are:")
flag.PrintDefaults()
}
func main() {
debug := flag.Bool("debug", false, "Enable debug logging")
flag.Usage = printUsage
flag.Parse()
args := flag.Args()
if len(args) != 2 {
printUsage()
os.Exit(1)
}
country := args[0]
out := args[1]
client := http.Client{
Transport: &CustomTransport{},
}
fetcher, ok := fetchers[country]
if !ok {
printUsage()
os.Exit(1)
}
logOptions := slog.HandlerOptions{}
if *debug {
logOptions.Level = slog.LevelDebug
}
logger = slog.New(slog.NewTextHandler(os.Stderr, &logOptions))
var progressReporter progress.Reporter
if term.IsTerminal(int(os.Stdout.Fd())) {
progressReporter = progress.NewReporter(os.Stdout)
} else {
progressReporter = progress.NewReporter(io.Discard)
}
laws, err := fetcher.Fetch(logger, &client, &progressReporter)
if err != nil {
logger.Error("fetching failed", "error", err)
os.Exit(1)
}
if len(laws) == 0 {
logger.Error("fetcher found 0 laws")
os.Exit(1)
}
file, err := os.Create(out)
if err != nil {
logger.Error("failed to create file", "err", err, "path", out)
os.Exit(1)
}
defer file.Close()
err = json.NewEncoder(file).Encode(laws)
if err != nil {
logger.Error("failed to encode laws as JSON", "err", err)
os.Exit(1)
}
socketPath := os.Getenv("SOCKET_PATH")
if socketPath == "" {
logger.Info("not notifyng lex-serve because SOCKET_PATH isn't set")
} else {
client = http.Client{
Transport: &http.Transport{
DialContext: func(ctx context.Context, network string, addr string) (net.Conn, error) {
return net.Dial("unix", socketPath)
},
},
}
resp, err := client.Get("http://internal.invalid/update?country=" + country)
if err != nil {
logger.Error("failed to update lex-serve", "err", err)
os.Exit(1)
}
if resp.StatusCode != 200 {
logger.Error("unexpected status code from lex-serve", "statusCode", resp.StatusCode)
os.Exit(1)
}
}
}
type CustomTransport struct{}
func (t *CustomTransport) RoundTrip(req *http.Request) (*http.Response, error) {
logger.Debug("request", "method", req.Method, "url", req.URL)
req.Header["User-Agent"] = []string{"lex-surf"}
return http.DefaultTransport.RoundTrip(req)
}
|