DirectoryReader takes a filesystem and an absolute directory path, and it returns a combined reader into that directory, and an offset map of the file contents. This is used to build a reader from a directory containing language source files, and as a result, this will skip over files that don't hav
(fs engine.Fs, dir string)
| 176 | // TODO: Due to an implementation difficulty, offsets are currently in length! |
| 177 | // NOTE: This was used for an older deprecated form of lex/parse file combining. |
| 178 | func DirectoryReader(fs engine.Fs, dir string) (io.Reader, map[uint64]string, error) { |
| 179 | fis, err := fs.ReadDir(dir) // ([]os.FileInfo, error) |
| 180 | if err != nil { |
| 181 | return nil, nil, errwrap.Wrapf(err, "can't stat directory contents `%s`", dir) |
| 182 | } |
| 183 | |
| 184 | var offset uint64 |
| 185 | offsets := make(map[uint64]string) // cumulative offset to abs. filename |
| 186 | readers := []io.Reader{} |
| 187 | |
| 188 | for _, fi := range fis { |
| 189 | if fi.IsDir() { |
| 190 | continue // skip directories |
| 191 | } |
| 192 | name := path.Join(dir, fi.Name()) // relative path made absolute |
| 193 | if !strings.HasSuffix(name, interfaces.DotFileNameExtension) { |
| 194 | continue |
| 195 | } |
| 196 | |
| 197 | f, err := fs.Open(name) // opens read-only |
| 198 | if err != nil { |
| 199 | return nil, nil, errwrap.Wrapf(err, "can't open file `%s`", name) |
| 200 | } |
| 201 | defer f.Close() |
| 202 | //stat, err := f.Stat() // (os.FileInfo, error) |
| 203 | //if err != nil { |
| 204 | // return nil, nil, errwrap.Wrapf(err, "can't stat file `%s`", name) |
| 205 | //} |
| 206 | |
| 207 | offsets[offset] = name // save cumulative offset (starts at 0) |
| 208 | //offset += uint64(stat.Size()) // the earlier stat causes file download |
| 209 | |
| 210 | // TODO: store the offset in size instead of length! we're using |
| 211 | // length at the moment since it is not clear how easy it is for |
| 212 | // the lexer/parser to return the byte offset as well as line no |
| 213 | // NOTE: in addition, this scanning is not the fastest for perf! |
| 214 | scanner := bufio.NewScanner(f) |
| 215 | lines := 0 |
| 216 | for scanner.Scan() { // each line |
| 217 | lines++ |
| 218 | } |
| 219 | if err := scanner.Err(); err != nil { |
| 220 | return nil, nil, errwrap.Wrapf(err, "can't scan file `%s`", name) |
| 221 | } |
| 222 | offset += uint64(lines) |
| 223 | if start, err := f.Seek(0, io.SeekStart); err != nil { // reset |
| 224 | return nil, nil, errwrap.Wrapf(err, "can't reset file `%s`", name) |
| 225 | } else if start != 0 { // we should be at the start (0) |
| 226 | return nil, nil, fmt.Errorf("reset of file `%s` was %d", name, start) |
| 227 | } |
| 228 | |
| 229 | readers = append(readers, f) |
| 230 | } |
| 231 | if len(offsets) == 0 { |
| 232 | // TODO: this condition should be validated during the deploy... |
| 233 | return nil, nil, fmt.Errorf("no files in main directory") |
| 234 | } |
| 235 |