99 "errors"
1010 "fmt"
1111 "io"
12+ "iter"
1213 "os"
1314 "sort"
1415 "sync"
@@ -45,29 +46,43 @@ type Store struct {
4546}
4647
4748// Load reads in all the records serialized in the provided [io.Reader].
49+ //
50+ // Deprecated: This just calls [NewLoader].
51+ //
52+ //go:fix inline
4853func Load (ctx context.Context , r io.Reader ) (* Loader , error ) {
54+ return NewLoader (ctx , r )
55+ }
56+
57+ // NewLoader returns a loader configured to read the records serialized in the
58+ // provided [io.Reader].
59+ func NewLoader (ctx context.Context , r io.Reader ) (* Loader , error ) {
4960 l := Loader {
5061 dec : json .NewDecoder (r ),
5162 cur : uuid .Nil ,
5263 }
5364 return & l , nil
5465}
5566
56- // Loader is an iterator that returns a series of [Entry] .
67+ // Loader is an iterator over serialized records .
5768//
58- // Users should call [* Loader.Next] until it reports false, then check for
59- // errors via [* Loader.Err].
69+ // Users should consume one of the [ Loader.Vulnerabilities] or
70+ // [Loader.Enrichments] iterators, then check for errors via [Loader.Err].
6071type Loader struct {
61- err error
62- e * Entry
72+ err error
73+ dec * json.Decoder
74+ started bool
6375
64- dec * json.Decoder
76+ // Used in the deprecated [Loader.Next]+[Loader.Entry] flow.
77+ e * Entry
6578 next * Entry
6679 de diskEntry
6780 cur uuid.UUID
6881}
6982
7083// Next reports whether there's an [Entry] to be processed.
84+ //
85+ // Deprecated: Use the [Loader.All] iterator.
7186func (l * Loader ) Next () bool {
7287 if l .err != nil {
7388 return false
@@ -113,10 +128,75 @@ func (l *Loader) Next() bool {
113128}
114129
115130// Entry returns the latest loaded [Entry].
131+ //
132+ // Deprecated: Use the [Loader.All] iterator.
116133func (l * Loader ) Entry () * Entry {
117134 return l .e
118135}
119136
137+ // All returns an iterator-of-iterators yielding all entries in the Loader.
138+ //
139+ // The inner iterator returns exactly one of the two values.
140+ func (l * Loader ) All () iter.Seq2 [* Entry , iter.Seq2 [* claircore.Vulnerability , * driver.EnrichmentRecord ]] {
141+ if l .started {
142+ l .err = errors .Join (l .err , fmt .Errorf ("attempted to re-use a Loader" ))
143+ return noopIterator
144+ }
145+
146+ // These are shared across the two iterators:
147+ cur := uuid .Nil
148+ var de diskEntry
149+
150+ // The inner iterator decodes every entry after the first one, stopping
151+ // iteration and passing control to the outer iterator when the Ref
152+ // changes.
153+ inner := func (yield func (* claircore.Vulnerability , * driver.EnrichmentRecord ) bool ) {
154+ for ; l .err == nil && cur == de .Ref ; l .err = l .dec .Decode (& de ) {
155+ var v * claircore.Vulnerability
156+ var e * driver.EnrichmentRecord
157+ switch de .Kind {
158+ case driver .VulnerabilityKind :
159+ v = getVulnerability ()
160+ l .err = json .Unmarshal (de .Vuln .buf , v )
161+ case driver .EnrichmentKind :
162+ e = getEnrichment ()
163+ l .err = json .Unmarshal (de .Enrichment .buf , e )
164+ }
165+ if l .err != nil || ! yield (v , e ) {
166+ return
167+ }
168+ }
169+ }
170+ // Outer reads the first entry and handles when the Ref changes.
171+ outer := func (yield func (* Entry , iter.Seq2 [* claircore.Vulnerability , * driver.EnrichmentRecord ]) bool ) {
172+ l .started = true
173+
174+ for l .err = l .dec .Decode (& de ); l .err == nil ; {
175+ ent := & Entry {
176+ CommonEntry : CommonEntry {
177+ Updater : de .Updater ,
178+ Fingerprint : de .Fingerprint ,
179+ Date : de .Date ,
180+ Kind : de .Kind ,
181+ },
182+ }
183+ cur = de .Ref
184+ if ! yield (ent , inner ) {
185+ return
186+ }
187+ for ; l .err == nil && cur == de .Ref ; l .err = l .dec .Decode (& de ) {
188+ // Skip entries if the inner iterator was not fully consumed.
189+ }
190+ }
191+ }
192+
193+ return outer
194+ }
195+
196+ // NoopIterator is an iterator that does nothing.
197+ func noopIterator (_ func (* Entry , iter.Seq2 [* claircore.Vulnerability , * driver.EnrichmentRecord ]) bool ) {
198+ }
199+
120200// Err is the latest encountered error.
121201func (l * Loader ) Err () error {
122202 // Don't report EOF as an error.
@@ -148,10 +228,10 @@ func (s *Store) Store(w io.Writer) error {
148228 shim := newBufShim (f )
149229 defer shim .Close ()
150230 for range ct {
231+ e .Kind = k
151232 dent := diskEntry {
152233 CommonEntry : e ,
153234 Ref : id ,
154- Kind : k ,
155235 }
156236 switch k {
157237 case driver .EnrichmentKind :
@@ -237,6 +317,7 @@ type CommonEntry struct {
237317 Updater string
238318 Fingerprint driver.Fingerprint
239319 Date time.Time
320+ Kind driver.UpdateKind
240321}
241322
242323// DiskEntry is a single vulnerability or enrichment. It's made from unpacking an
@@ -249,7 +330,6 @@ type diskEntry struct {
249330 Ref uuid.UUID
250331 Vuln * bufShim `json:",omitempty"`
251332 Enrichment * bufShim `json:",omitempty"`
252- Kind driver.UpdateKind
253333}
254334
255335// Entries returns a map containing all the Entries stored by calls to
@@ -477,6 +557,7 @@ func getBuf() []byte {
477557 }
478558 return b
479559}
560+
480561func putBuf (b []byte ) {
481562 bufPool .Put (b )
482563}
0 commit comments