-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathfile.go
142 lines (121 loc) · 2.86 KB
/
file.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
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
134
135
136
137
138
139
140
141
142
// Copyright (c) 2020, The Emergent Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
import (
"bufio"
"os"
"strings"
"time"
)
// File represents one opened file -- all data is read in and maintained here
type File struct {
// file name (either in same dir or include path)
FName string `desc:"file name (either in same dir or include path)"`
// mod time of file when last read
ModTime time.Time `desc:"mod time of file when last read"`
// delim is commas, not tabs
Commas bool `desc:"delim is commas, not tabs"`
// rows of data == len(Data)
Rows int `desc:"rows of data == len(Data)"`
// width of each column: resized to fit widest element
Widths []int `desc:"width of each column: resized to fit widest element"`
// headers
Heads []string `desc:"headers"`
// data -- rows 1..end
Data [][]string `desc:"data -- rows 1..end"`
}
// Files is a slice of open files
type Files []*File
// TheFiles are the set of open files
var TheFiles Files
// Open opens file, reads it
func (fl *File) Open(fname string) error {
fl.FName = fname
return fl.Read()
}
// CheckUpdate checks if file has been modified -- returns true if so
func (fl *File) CheckUpdate() bool {
st, err := os.Stat(fl.FName)
if err != nil {
return false
}
return st.ModTime().After(fl.ModTime)
}
// Read reads data from file
func (fl *File) Read() error {
st, err := os.Stat(fl.FName)
if err != nil {
return err
}
fl.ModTime = st.ModTime()
f, err := os.Open(fl.FName)
if err != nil {
return err
}
defer f.Close()
if fl.Data != nil {
fl.Data = fl.Data[:0]
}
scan := bufio.NewScanner(f)
ln := 0
for scan.Scan() {
s := string(scan.Bytes())
var fd []string
if fl.Commas {
fd = strings.Split(s, ",")
} else {
fd = strings.Split(s, "\t")
}
if ln == 0 {
if len(fd) == 0 || strings.Count(s, ",") > strings.Count(s, "\t") {
fl.Commas = true
fd = strings.Split(s, ",")
}
fl.Heads = fd
fl.Widths = make([]int, len(fl.Heads))
fl.FitWidths(fd)
ln++
continue
}
fl.Data = append(fl.Data, fd)
fl.FitWidths(fd)
ln++
}
fl.Rows = ln - 1 // skip header
return err
}
// FitWidths expands widths given current set of fields
func (fl *File) FitWidths(fd []string) {
nw := len(fl.Widths)
for i, f := range fd {
if i >= nw {
break
}
w := max(fl.Widths[i], len(f))
fl.Widths[i] = w
}
}
/////////////////////////////////////////////////////////////////
// Files
// Open opens all files
func (fl *Files) Open(fnms []string) {
for _, fn := range fnms {
f := &File{}
err := f.Open(fn)
if err == nil {
*fl = append(*fl, f)
}
}
}
// CheckUpdates check for any updated files, re-read if so -- returns true if so
func (fl *Files) CheckUpdates() bool {
got := false
for _, f := range *fl {
if f.CheckUpdate() {
f.Read()
got = true
}
}
return got
}