Merge branch 'master' into 6465-optimize-workbench-integration-tests
[arvados.git] / sdk / go / manifest / manifest.go
1 /* Deals with parsing Manifest Text. */
2
3 // Inspired by the Manifest class in arvados/sdk/ruby/lib/arvados/keep.rb
4
5 package manifest
6
7 import (
8         "git.curoverse.com/arvados.git/sdk/go/blockdigest"
9         "log"
10         "strings"
11 )
12
13 type Manifest struct {
14         Text string
15 }
16
17 // Represents a single line from a manifest.
18 type ManifestStream struct {
19         StreamName string
20         Blocks     []string
21         Files      []string
22 }
23
24 func parseManifestStream(s string) (m ManifestStream) {
25         tokens := strings.Split(s, " ")
26         m.StreamName = tokens[0]
27         tokens = tokens[1:]
28         var i int
29         for i = range tokens {
30                 if !blockdigest.IsBlockLocator(tokens[i]) {
31                         break
32                 }
33         }
34         m.Blocks = tokens[:i]
35         m.Files = tokens[i:]
36         return
37 }
38
39 func (m *Manifest) StreamIter() <-chan ManifestStream {
40         ch := make(chan ManifestStream)
41         go func(input string) {
42                 // This slice holds the current line and the remainder of the
43                 // manifest.  We parse one line at a time, to save effort if we
44                 // only need the first few lines.
45                 lines := []string{"", input}
46                 for {
47                         lines = strings.SplitN(lines[1], "\n", 2)
48                         if len(lines[0]) > 0 {
49                                 // Only parse non-blank lines
50                                 ch <- parseManifestStream(lines[0])
51                         }
52                         if len(lines) == 1 {
53                                 break
54                         }
55                 }
56                 close(ch)
57         }(m.Text)
58         return ch
59 }
60
61 // Blocks may appear mulitple times within the same manifest if they
62 // are used by multiple files. In that case this Iterator will output
63 // the same block multiple times.
64 func (m *Manifest) BlockIterWithDuplicates() <-chan blockdigest.BlockLocator {
65         blockChannel := make(chan blockdigest.BlockLocator)
66         go func(streamChannel <-chan ManifestStream) {
67                 for m := range streamChannel {
68                         for _, block := range m.Blocks {
69                                 if b, err := blockdigest.ParseBlockLocator(block); err == nil {
70                                         blockChannel <- b
71                                 } else {
72                                         log.Printf("ERROR: Failed to parse block: %v", err)
73                                 }
74                         }
75                 }
76                 close(blockChannel)
77         }(m.StreamIter())
78         return blockChannel
79 }