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

rokath / trice / 20314806283

17 Dec 2025 07:12PM UTC coverage: 46.561% (-9.6%) from 56.117%
20314806283

push

github

rokath
minor corrections

2532 of 5438 relevant lines covered (46.56%)

868.71 hits per line

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

37.05
/internal/args/handler.go
1
// Copyright 2020 Thomas.Hoehenleitner [at] seerose.net
2
// Use of this source code is governed by a license that can be found in the LICENSE file.
3

4
// Package args implements the commandline interface and calls the appropriate commands.
5
package args
6

7
import (
8
        "errors"
9
        "flag"
10
        "fmt"
11
        "io"
12
        "os"
13
        "runtime/debug"
14
        "strings"
15
        "sync"
16
        "time"
17

18
        "github.com/kr/pretty"
19
        "github.com/rokath/trice/internal/com"
20
        "github.com/rokath/trice/internal/decoder"
21
        "github.com/rokath/trice/internal/do"
22
        "github.com/rokath/trice/internal/emitter"
23
        "github.com/rokath/trice/internal/id"
24
        "github.com/rokath/trice/internal/receiver"
25
        "github.com/rokath/trice/internal/translator"
26
        "github.com/rokath/trice/pkg/cipher"
27
        "github.com/rokath/trice/pkg/msg"
28
        "github.com/spf13/afero"
29
)
30

31
// Handler is called in main, evaluates args and calls the appropriate functions.
32
// It returns for program exit.
33
func Handler(w io.Writer, fSys *afero.Afero, args []string) error {
6✔
34
        // Trim leading and trailing whitespace
6✔
35
        //for i := range args {
6✔
36
        //        args[i] = strings.TrimSpace(args[i])
6✔
37
        //}
6✔
38

6✔
39
        if Date == "" { // goreleaser will set Date, otherwise use file info.
7✔
40
                path, err := os.Executable()
1✔
41
                if err != nil {
1✔
42
                        Date = fmt.Sprint(err) // just in case, simply show the error
×
43
                } else {
1✔
44
                        fi, err := fSys.Stat(path)
1✔
45
                        if err == nil { // On running main tests file-info is invalid, so do not use it in that case.
1✔
46
                                Date = fi.ModTime().String()
×
47
                        } else {
1✔
48
                                Date = fmt.Sprint(err) // just in case, simply show the error
1✔
49
                        }
1✔
50
                }
51
        }
52

53
        // Verify that a sub-command has been provided: os.Arg[0] is the main command (trice), os.Arg[1] will be the sub-command.
54
        if len(args) < 2 {
7✔
55
                m := "no args, try: '" + args[0] + " help'"
1✔
56
                return errors.New(m)
1✔
57
        }
1✔
58

59
        // Switch on the sub-command. Parse the flags for appropriate FlagSet.
60
        // FlagSet.Parse() requires a set of arguments to parse as input.
61
        // os.Args[2:] will be all arguments starting after the sub-command at os.Args[1]
62
        subCmd := args[1]
5✔
63
        subArgs := args[2:]
5✔
64
        switch subCmd { // Check which sub-command is invoked.
5✔
65
        default:
2✔
66
                return fmt.Errorf("unknown sub-command '%s'. try: '%s help|h'", subCmd, args[0])
2✔
67
        case "h", "help":
1✔
68
                msg.OnErr(fsScHelp.Parse(subArgs))
1✔
69
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
1✔
70
                return scHelp(w)
1✔
71
        case "s", "scan":
1✔
72
                msg.OnErr(fsScScan.Parse(subArgs))
1✔
73
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
1✔
74
                _, err := com.GetSerialPorts(w)
1✔
75
                return err
1✔
76
        case "ver", "version":
×
77
                msg.OnErr(fsScVersion.Parse(subArgs))
×
78
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
79
                if Verbose {
×
80
                        bi, ok := debug.ReadBuildInfo()
×
81
                        if !ok {
×
82
                                fmt.Println("buildInfo not ok")
×
83
                        } else {
×
84
                                pretty.Println(bi)
×
85
                        }
×
86
                }
87
                return scVersion(w)
×
88
        case "a", "add":
×
89
                msg.OnErr(fsScAdd.Parse(subArgs))
×
90
                id.CompactSrcs()
×
91
                id.ProcessAliases()
×
92
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
93
                return id.SubCmdIdAdd(w, fSys)
×
94
        case "generate":
×
95
                msg.OnErr(fsScGenerate.Parse(subArgs))
×
96
                id.CompactSrcs()
×
97
                id.ProcessAliases()
×
98
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
99
                return id.SubCmdGenerate(w, fSys)
×
100
        case "i", "insert":
×
101
                msg.OnErr(fsScInsert.Parse(subArgs))
×
102
                id.CompactSrcs()
×
103
                id.ProcessAliases()
×
104
                emitter.AddUserLabels()
×
105
                err := id.EvaluateIDRangeStrings()
×
106
                if err != nil {
×
107
                        return err
×
108
                }
×
109
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
110
                //fmt.Println(id.StructuredLoggingFormatString)
×
111
                //fmt.Println(id.StructuredLoggingValuesString)
×
112
                //return err
×
113
                return id.SubCmdIdInsert(w, fSys)
×
114
        case "c", "clean":
×
115
                msg.OnErr(fsScClean.Parse(subArgs))
×
116
                id.CompactSrcs()
×
117
                id.ProcessAliases()
×
118
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
119
                return id.SubCmdIdClean(w, fSys)
×
120
        case "sd", "shutdown":
×
121
                msg.OnErr(fsScSdSv.Parse(subArgs))
×
122
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
123
                return emitter.ScShutdownRemoteDisplayServer(w, 0) // 0|1: 0=no 1=with shutdown timestamp in display server
×
124
        case "ds", "displayServer":
×
125
                msg.OnErr(fsScSv.Parse(subArgs))
×
126
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
×
127
                return emitter.ScDisplayServer(w) // endless loop
×
128
        case "l", "log":
1✔
129
                id.Logging = true
1✔
130
                msg.OnErr(fsScLog.Parse(subArgs))
1✔
131
                id.ProcessAliases()
1✔
132
                emitter.AddUserLabels()
1✔
133
                decoder.TargetTimeStampUnitPassed = isLogFlagPassed("ts")
1✔
134
                decoder.ShowTargetStamp32Passed = isLogFlagPassed("ts32")
1✔
135
                decoder.ShowTargetStamp16Passed = isLogFlagPassed("ts16")
1✔
136
                decoder.ShowTargetStamp0Passed = isLogFlagPassed("ts0")
1✔
137
                w = do.DistributeArgs(w, fSys, LogfileName, Verbose)
1✔
138
                logLoop(w, fSys) // endless loop
1✔
139
                return nil
1✔
140
        }
141
}
142

143
// https://stackoverflow.com/questions/35809252/check-if-flag-was-provided-in-go
144
func isLogFlagPassed(name string) bool {
4✔
145
        found := false
4✔
146
        fsScLog.Visit(func(f *flag.Flag) {
12✔
147
                if f.Name == name {
8✔
148
                        found = true
×
149
                }
×
150
        })
151
        return found
4✔
152
}
153

154
type selector struct {
155
        flag bool
156
        info func(io.Writer) error
157
}
158

159
// logLoop prepares writing and lut and provides a retry mechanism for unplugged UART.
160
func logLoop(w io.Writer, fSys *afero.Afero) {
1✔
161
        msg.FatalOnErr(cipher.SetUp(w)) // does nothing when -password is ""
1✔
162
        if decoder.TestTableMode {
1✔
163
                // set switches if they not set already
×
164
                // trice l -ts off -prefix " }, ``" -suffix "\n``}," -color off
×
165
                if emitter.HostStamp == "LOCmicro" {
×
166
                        emitter.HostStamp = "off"
×
167
                }
×
168
                if defaultPrefix == emitter.Prefix {
×
169
                        emitter.Prefix = " }, `"
×
170
                }
×
171
                if emitter.Suffix == "" {
×
172
                        emitter.Suffix = "`},"
×
173
                }
×
174
                if emitter.ColorPalette == "default" {
×
175
                        emitter.ColorPalette = "off"
×
176
                }
×
177
                decoder.TargetStamp32 = "" // todo: justify this line
×
178
        }
179

180
        var ilu id.TriceIDLookUp
1✔
181
        if id.FnJSON == "emptyFile" { // reserved name for tests only
2✔
182
                ilu = make(id.TriceIDLookUp)
1✔
183
        } else {
1✔
184
                ilu = id.NewLut(w, fSys, id.FnJSON) // lut is a map, that means a pointer
×
185
                decoder.IDLUT = ilu
×
186
        }
×
187
        m := new(sync.RWMutex) // m is a pointer to a read write mutex for lu
1✔
188
        m.Lock()
1✔
189
        ilu.AddFmtCount(w)
1✔
190
        m.Unlock()
1✔
191
        // Just in case the id list file FnJSON gets updated, the file watcher updates lut.
1✔
192
        // This way trice needs NOT to be restarted during development process.
1✔
193
        //go ilu.FileWatcher(w, fSys, m)
1✔
194

1✔
195
        var li id.TriceIDLookUpLI // nil
1✔
196

1✔
197
        if id.LIFnJSON == "emptyFile" { // reserved name for tests only
1✔
198
                li = make(id.TriceIDLookUpLI) // li is not nil, but empty
×
199
        } else {
1✔
200
                if _, err := fSys.Stat(id.LIFnJSON); errors.Is(err, os.ErrNotExist) {
2✔
201
                        if id.LIFnJSON != "off" && id.LIFnJSON != "none" && id.LIFnJSON != "no" {
2✔
202
                                if Verbose {
1✔
203
                                        fmt.Fprintf(w, "path/to/ %s does not exist: li is nil\n", id.LIFnJSON)
×
204
                                }
×
205
                        }
206
                } else {
×
207
                        li = id.NewLutLI(w, fSys, id.LIFnJSON) // lut is a map, that means a pointer
×
208
                        decoder.LILUT = li
×
209
                        // Just in case the id location information file LIFnJSON gets updated, the file watcher updates li.
×
210
                        // This way trice needs NOT to be restarted during development process.
×
211
                        //go li.FileWatcher(w, fSys)
×
212
                }
×
213
        }
214

215
        sw := emitter.New(w)
1✔
216
        var interrupted bool
1✔
217
        var counter int
1✔
218

1✔
219
        for {
2✔
220
                rwc, e := receiver.NewReadWriteCloser(w, fSys, Verbose, receiver.Port, receiver.PortArguments)
1✔
221
                if e != nil {
2✔
222
                        fmt.Fprintln(w, e)
1✔
223
                        if !interrupted {
2✔
224
                                return // hopeless
1✔
225
                        }
1✔
226
                        time.Sleep(1000 * time.Millisecond) // retry interval
×
227
                        fmt.Fprintf(w, "\rsig:(re-)setup input port...%d", counter)
×
228
                        counter++
×
229
                        continue
×
230
                }
231
                defer func() { msg.OnErr(rwc.Close()) }()
×
232
                interrupted = true
×
233
                if receiver.ShowInputBytes {
×
234
                        rwc = receiver.NewBytesViewer(w, rwc)
×
235
                }
×
236
                if receiver.BinaryLogfileName != "off" && receiver.BinaryLogfileName != "none" {
×
237
                        rwc = receiver.NewBinaryLogger(w, fSys, rwc)
×
238
                }
×
239
                e = translator.Translate(w, sw, ilu, m, li, rwc)
×
240
                if io.EOF == e {
×
241
                        return // end of predefined buffer
×
242
                }
×
243
        }
244
}
245

246
// scVersion is sub-command 'version'. It prints version information.
247
func scVersion(w io.Writer) error {
×
248
        if Verbose {
×
249
                fmt.Fprintln(w, "https://github.com/rokath/trice")
×
250
        }
×
251

252
        // Fallback if nothing has been set (e.g., go install without ldflags)
253
        if Version == "" && Commit == "" && Date == "" && Branch == "" {
×
254
                fmt.Fprintln(w, "version=dev (no build info)")
×
255
                return nil
×
256
        }
×
257

258
        dirtyText := ""
×
259
        if GitState == "dirty" {
×
260
                dirtyText = " (local modifications at build time)"
×
261
        }
×
262

263
        // If version is set (GoReleaser or local build)
264
        if Version != "" {
×
265
                // GoReleaser builds usually don't set Branch/GitState/GitStatus.
×
266
                // For those, we omit the branch to avoid "branch=" being empty.
×
267
                if Branch != "" {
×
268
                        fmt.Fprintf(w, "no version, branch=%s%s, commit=%s, built at %s", Branch, dirtyText, Commit, Date)
×
269
                } else {
×
270
                        fmt.Fprintf(w, "version=%s, commit=%s, built at %s",
×
271
                                Version, Commit, Date)
×
272
                }
×
273

274
                // Optional: append builtBy if present.
275
                if BuiltBy != "" {
×
276
                        fmt.Fprintf(w, " (built by %s)", BuiltBy)
×
277
                }
×
278
                fmt.Fprintln(w)
×
279

280
        } else {
×
281
                // Fallback to Branch/Commit for local dev builds with missing version
×
282
                if Branch != "" {
×
283
                        fmt.Fprintf(w, "branch=%s%s, ", Branch, dirtyText)
×
284
                }
×
285
                if Commit != "" {
×
286
                        fmt.Fprintf(w, "commit=%s, ", Date)
×
287
                }
×
288
                fmt.Fprintf(w, "built at %s\n", Date)
×
289
        }
290

291
        // Only with verbose + dirty: display list of files that were modified at build time.
292
        if Verbose && GitState == "dirty" && GitStatus != "" {
×
293
                fmt.Fprintln(w, "modified files at build time:")
×
294
                for _, line := range strings.Split(GitStatus, "|") {
×
295
                        line = strings.TrimSpace(line)
×
296
                        if line == "" {
×
297
                                continue
×
298
                        }
299
                        fmt.Fprintf(w, "  %s\n", line)
×
300
                }
301
        }
302

303
        return nil
×
304
}
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