|
1
|
package main |
|
2
|
|
|
3
|
import ( |
|
4
|
"bufio" |
|
5
|
"errors" |
|
6
|
"flag" |
|
7
|
"fmt" |
|
8
|
"os" |
|
9
|
"strings" |
|
10
|
) |
|
11
|
|
|
12
|
type Sense struct { |
|
13
|
Definition string |
|
14
|
Examples []string |
|
15
|
} |
|
16
|
|
|
17
|
type POSBlock struct { |
|
18
|
POS string |
|
19
|
IPA string |
|
20
|
Senses []Sense |
|
21
|
} |
|
22
|
|
|
23
|
type Entry struct { |
|
24
|
Word string |
|
25
|
RedirectWord string |
|
26
|
POSBlocks []POSBlock |
|
27
|
} |
|
28
|
|
|
29
|
func isPiped() bool { |
|
30
|
fi, _ := os.Stdin.Stat() |
|
31
|
return (fi.Mode() & os.ModeCharDevice) == 0 |
|
32
|
} |
|
33
|
|
|
34
|
func readStdinWord() (string, error) { |
|
35
|
s := bufio.NewScanner(os.Stdin) |
|
36
|
if s.Scan() { |
|
37
|
return strings.TrimSpace(s.Text()), nil |
|
38
|
} |
|
39
|
return "", s.Err() |
|
40
|
} |
|
41
|
|
|
42
|
func main() { |
|
43
|
all := flag.Bool("all", false, "print all senses to stderr") |
|
44
|
aFlag := flag.Bool("a", false, "shorthand for --all") |
|
45
|
pos := flag.String("pos", "", "filter by part of speech") |
|
46
|
clearCacheFlag := flag.Bool("clear-cache", false, "clear cache for word") |
|
47
|
|
|
48
|
flag.Parse() |
|
49
|
|
|
50
|
args := flag.Args() |
|
51
|
|
|
52
|
var word string |
|
53
|
switch { |
|
54
|
case len(args) > 0: |
|
55
|
word = args[0] |
|
56
|
case isPiped(): |
|
57
|
var err error |
|
58
|
word, err = readStdinWord() |
|
59
|
if err != nil { |
|
60
|
fmt.Fprintf(os.Stderr, "anpi: %v\n", err) |
|
61
|
os.Exit(1) |
|
62
|
} |
|
63
|
if word == "" { |
|
64
|
fmt.Fprintln(os.Stderr, "usage: anpi [flags] <word>") |
|
65
|
os.Exit(1) |
|
66
|
} |
|
67
|
default: |
|
68
|
fmt.Fprintln(os.Stderr, "usage: anpi [flags] <word>") |
|
69
|
os.Exit(1) |
|
70
|
} |
|
71
|
|
|
72
|
if len(args) > 1 { |
|
73
|
for _, arg := range args[1:] { |
|
74
|
if arg == "--all" || arg == "-a" { |
|
75
|
*all = true |
|
76
|
} |
|
77
|
if arg == "--clear-cache" { |
|
78
|
*clearCacheFlag = true |
|
79
|
} |
|
80
|
} |
|
81
|
} |
|
82
|
|
|
83
|
if *all || *aFlag { |
|
84
|
*all = true |
|
85
|
} |
|
86
|
|
|
87
|
cache, err := NewCache() |
|
88
|
if err != nil { |
|
89
|
fmt.Fprintln(os.Stderr, err) |
|
90
|
os.Exit(1) |
|
91
|
} |
|
92
|
|
|
93
|
if *clearCacheFlag { |
|
94
|
_ = cache.Clear(word) |
|
95
|
} |
|
96
|
|
|
97
|
entry, err := cache.Read(word) |
|
98
|
if errors.Is(err, ErrNotFound) { |
|
99
|
_, _ = fmt.Fprintf(os.Stderr, "anpi: %v\n", err) |
|
100
|
os.Exit(1) |
|
101
|
} |
|
102
|
if err != nil { |
|
103
|
dict := NewCambridgeDictionary() |
|
104
|
entry, err = dict.Lookup(word) |
|
105
|
if err != nil { |
|
106
|
if werr := cache.Write(word, nil, false); werr != nil { |
|
107
|
fmt.Fprintf(os.Stderr, "cache write error: %v\n", werr) |
|
108
|
} |
|
109
|
_, _ = fmt.Fprintf(os.Stderr, "anpi: %v\n", err) |
|
110
|
os.Exit(1) |
|
111
|
} |
|
112
|
if err := cache.Write(word, entry, true); err != nil { |
|
113
|
fmt.Fprintf(os.Stderr, "cache write error: %v\n", err) |
|
114
|
} |
|
115
|
} |
|
116
|
|
|
117
|
if *pos != "" { |
|
118
|
var filtered []POSBlock |
|
119
|
for _, b := range entry.POSBlocks { |
|
120
|
if strings.EqualFold(b.POS, *pos) { |
|
121
|
filtered = append(filtered, b) |
|
122
|
} |
|
123
|
} |
|
124
|
if len(filtered) == 0 { |
|
125
|
_, _ = fmt.Fprintf(os.Stderr, "warning: no %q sense found, showing all\n", *pos) |
|
126
|
} else { |
|
127
|
entry.POSBlocks = filtered |
|
128
|
} |
|
129
|
} |
|
130
|
|
|
131
|
if *all { |
|
132
|
writeAll(entry, os.Stderr) |
|
133
|
return |
|
134
|
} |
|
135
|
|
|
136
|
fmt.Print(toTSV(entry)) |
|
137
|
} |