/
processor.go
107 lines (90 loc) · 2.66 KB
/
processor.go
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
package gonverge
import (
"bufio"
"fmt"
"os"
"strings"
)
// procState is the state of the file processor.
type procState int
const (
// procStateCoding represents the file processor state
// when processing a code block.
procStateCoding procState = iota
// procStateImporting represents the file processor state
// when processing an import block.
procStateImporting
)
const (
// tokenPkgDecl is the token for a package declaration line.
tokenPkgDecl string = `package `
// tokenImport is the token for import declarations.
tokenImport = `import`
// tokenImportMono is the token for a single import line.
tokenImportMono = tokenImport + ` "`
// tokenImportMulti is the token that starts an import block.
tokenImportMultiStart = tokenImport + ` (`
// tokenImportMultiEnd is the token that ends an import block.
tokenImportMultiFinish = `)`
)
// fileProcessor holds the *os.File representations
// of the command line arguments.
type fileProcessor struct {
// filePath is the path to the file to process.
filePath string
// state determines how the current
// line should be processed.
state procState
}
// newFileProcessor returns a new fileProcessor.
func newFileProcessor(filePath string) *fileProcessor {
return &fileProcessor{
filePath: filePath,
}
}
// process handles opening, parsing, and aggregating
// the contents of the file into a goFile.
func (p *fileProcessor) process() (*goFile, error) {
file, err := os.Open(p.filePath)
if err != nil {
return nil, fmt.Errorf("failed to open file: %w", err)
}
res := newGoFile()
scanner := bufio.NewScanner(file)
for scanner.Scan() {
switch line := scanner.Text(); {
case strings.HasPrefix(line, tokenPkgDecl):
res.pkgName = strings.TrimPrefix(line, tokenPkgDecl)
p.state = procStateCoding
case strings.HasPrefix(line, tokenImportMultiStart):
p.state = procStateImporting
case strings.HasPrefix(line, tokenImportMono):
res.addImport(strings.TrimPrefix(line, tokenImport))
case p.importing() && strings.HasSuffix(line, tokenImportMultiFinish):
p.state = procStateCoding
case p.importing():
if line == "" {
continue
}
res.addImport(line)
case p.coding():
res.appendCode(line)
default:
res.appendCode(line)
}
}
if err = scanner.Err(); err != nil {
return nil, fmt.Errorf("failed to read file: %w", err)
}
return res, nil
}
// importing returns true if the filePath processor is currently
// processing an import block.
func (p *fileProcessor) importing() bool {
return p.state == procStateImporting
}
// coding returns true if the filePath processor is currently
// processing a code block.
func (p *fileProcessor) coding() bool {
return p.state == procStateCoding
}