• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

ikawaha / kagome / 22607985497

03 Mar 2026 04:13AM UTC coverage: 85.247%. First build
22607985497

Pull #387

github

ikawaha
feat(server): improve lattice zoom anchoring and morpheme selection
Pull Request #387: feat(server): redesign demo UI with real-time lattice and zoom

23 of 41 new or added lines in 2 files covered. (56.1%)

1329 of 1559 relevant lines covered (85.25%)

3107.44 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

87.25
/cmd/server/cmd.go
1
package server
2

3
import (
4
        "context"
5
        "flag"
6
        "fmt"
7
        "io"
8
        "io/fs"
9
        "log"
10
        "net/http"
11
        "os"
12
        "os/signal"
13
        "strings"
14
        "syscall"
15
        "time"
16

17
        "github.com/ikawaha/kagome-dict/dict"
18
        "github.com/ikawaha/kagome-dict/ipa"
19
        "github.com/ikawaha/kagome-dict/uni"
20
        "github.com/ikawaha/kagome/v2/tokenizer"
21
)
22

23
// Stderr is the standard error writer.
24
var Stderr io.Writer = os.Stderr
25

26
// staticFS is an http.FileSystem that serves only non-HTML files and
27
// denies directory listings. It is used to expose /asset/ without leaking
28
// server-side template sources (demo.html, graph.html).
29
type staticFS struct {
30
        base http.FileSystem
31
}
32

NEW
33
func (s staticFS) Open(name string) (http.File, error) {
×
NEW
34
        if strings.HasSuffix(name, ".html") {
×
NEW
35
                return nil, fs.ErrNotExist
×
NEW
36
        }
×
NEW
37
        return s.base.Open(name)
×
38
}
39

40
// subcommand property
41
var (
42
        CommandName  = "server"
43
        Description  = `run tokenize server`
44
        usageMessage = "%s [-http=:6060] [-userdict userdic_file] [-dict (ipa|uni)]"
45
)
46

47
// options
48
type option struct {
49
        http    string
50
        dict    string
51
        udict   string
52
        flagSet *flag.FlagSet
53
}
54

55
// ContinueOnError ErrorHandling // Return a descriptive error.
56
// ExitOnError                   // Call os.Exit(2).
57
// PanicOnError                  // Call panic with a descriptive error.flag.ContinueOnError
58
func newOption(w io.Writer, eh flag.ErrorHandling) *option {
11✔
59
        ret := &option{
11✔
60
                flagSet: flag.NewFlagSet(CommandName, eh),
11✔
61
        }
11✔
62
        // option settings
11✔
63
        ret.flagSet.SetOutput(w)
11✔
64
        ret.flagSet.StringVar(&ret.http, "http", ":6060", "HTTP service address")
11✔
65
        ret.flagSet.StringVar(&ret.udict, "userdict", "", "user dict")
11✔
66
        ret.flagSet.StringVar(&ret.dict, "dict", "ipa", "system dict type (ipa|uni)")
11✔
67
        return ret
11✔
68
}
11✔
69

70
func (o *option) parse(args []string) error {
8✔
71
        if err := o.flagSet.Parse(args); err != nil {
9✔
72
                return err
1✔
73
        }
1✔
74
        // validations
75
        if nonFlag := o.flagSet.Args(); len(nonFlag) != 0 {
10✔
76
                return fmt.Errorf("invalid argument: %v", nonFlag)
3✔
77
        }
3✔
78
        if o.dict != "" && o.dict != "ipa" && o.dict != "uni" {
5✔
79
                return fmt.Errorf("invalid argument: -dict %v", o.dict)
1✔
80
        }
1✔
81
        return nil
3✔
82
}
83

84
// OptionCheck receives a slice of args and returns an error if it was not successfully parsed
85
func OptionCheck(args []string) error {
4✔
86
        opt := newOption(io.Discard, flag.ContinueOnError)
4✔
87
        if err := opt.parse(args); err != nil {
6✔
88
                return fmt.Errorf("%v, %w", CommandName, err)
2✔
89
        }
2✔
90
        return nil
2✔
91
}
92

93
func selectDict(name string) (*dict.Dict, error) {
3✔
94
        switch name {
3✔
95
        case "ipa":
2✔
96
                return ipa.Dict(), nil
2✔
97
        case "uni":
×
98
                return uni.Dict(), nil
×
99
        }
100
        return nil, fmt.Errorf("unknown name type, %v", name)
1✔
101
}
102

103
func command(ctx context.Context, opt *option) error {
3✔
104
        d, err := selectDict(opt.dict)
3✔
105
        if err != nil {
4✔
106
                return err
1✔
107
        }
1✔
108
        udict := tokenizer.Nop()
2✔
109
        if opt.udict != "" {
4✔
110
                d, err := dict.NewUserDict(opt.udict)
2✔
111
                if err != nil {
2✔
112
                        return err
×
113
                }
×
114
                udict = tokenizer.UserDict(d)
2✔
115
        }
116
        t, err := tokenizer.New(d, udict)
2✔
117
        if err != nil {
2✔
118
                return err
×
119
        }
×
120

121
        mux := http.NewServeMux()
2✔
122
        mux.Handle("/asset/", http.FileServer(staticFS{http.FS(assetFS)}))
2✔
123
        mux.Handle("/", &TokenizeDemoHandler{tokenizer: t})
2✔
124
        mux.Handle("/tokenize", &TokenizeHandler{tokenizer: t})
2✔
125
        mux.Handle("/lattice", &LatticeHandler{tokenizer: t})
2✔
126
        srv := http.Server{
2✔
127
                Addr:              opt.http,
2✔
128
                Handler:           mux,
2✔
129
                ReadHeaderTimeout: 20 * time.Second,
2✔
130
        }
2✔
131
        ch := make(chan error)
2✔
132
        go func() {
4✔
133
                log.Println(opt.http)
2✔
134
                ch <- srv.ListenAndServe()
2✔
135
        }()
2✔
136

137
        select {
2✔
138
        case <-ctx.Done():
2✔
139
                log.Printf("shutdown, %v", ctx.Err())
2✔
140
        case err := <-ch:
×
141
                return fmt.Errorf("server error, %w", err)
×
142
        }
143
        log.Printf("shutting down HTTP server at %q", opt.http)
2✔
144

2✔
145
        return nil
2✔
146
}
147

148
// Run receives the slice of args and executes the server
149
func Run(ctx context.Context, args []string) error {
4✔
150
        opt := newOption(Stderr, flag.ContinueOnError)
4✔
151
        if err := opt.parse(args); err != nil {
7✔
152
                Usage()
3✔
153
                PrintDefaults(flag.ContinueOnError)
3✔
154
                return fmt.Errorf("option parse error: %w", err)
3✔
155
        }
3✔
156
        ctx, cancel := context.WithCancel(ctx)
1✔
157
        defer cancel()
1✔
158
        go func() {
2✔
159
                c := make(chan os.Signal, 1)
1✔
160
                signal.Notify(c, syscall.SIGHUP, syscall.SIGINT, syscall.SIGTERM)
1✔
161
                <-c
1✔
162
                cancel()
1✔
163
        }()
1✔
164
        return command(ctx, opt)
1✔
165
}
166

167
// Usage provides information on the use of the server
168
func Usage() {
4✔
169
        fmt.Fprintf(Stderr, usageMessage+"\n", CommandName)
4✔
170
}
4✔
171

172
// PrintDefaults prints out the default flags
173
func PrintDefaults(eh flag.ErrorHandling) {
3✔
174
        o := newOption(Stderr, eh)
3✔
175
        o.flagSet.PrintDefaults()
3✔
176
}
3✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc