// Test the Next() method for the given iterator. // Return the number of elements encountered. func testNext(iter origins.Iterator) (int, error) { var ( i int f *origins.Fact ) for { if f = iter.Next(); f == nil { break } i++ } if err := iter.Err(); err != nil { return 0, err } return i, nil }
// Timeline returns an ordered set of events derived from the fact iterator. // The iterator is assumed to return facts in reverse order by time (newest first) // which is what the Log view returns. func Timeline(iter origins.Iterator, order Order) ([]*Event, error) { var ( key [4]string err error fact, prev, next *origins.Fact events []*Event event *Event etype EventType ) // Map facts keyed by entity/attribute pairs. // TODO: benchmark compared to cached identity approach. This is more // resilient since it does not rely on pointers. facts := make(map[[4]string]*origins.Fact) for { if fact = iter.Next(); fact == nil { break } // Uniquely identities an entity-attribute pair. The fact domain // is not considered here since that can be controlled by the passed // iterator. key = [4]string{ fact.Entity.Domain, fact.Entity.Name, fact.Attribute.Domain, fact.Attribute.Name, } // Swap depending on order. switch order { case Ascending: next = fact prev = facts[key] case Descending: prev = fact next = facts[key] default: logrus.Panicf("view: unknown order %v", order) } // Update the cache with the current fact. facts[key] = fact // No existing fact. Construct add event if prev == nil || next == nil { etype = Add // The next fact is a retraction. Since facts are ordered by // time, this assumes the retraction applies to the same value. } else if next.Operation == origins.Retraction { etype = Remove // If the value differs, mark as a change event. } else if !next.Value.Is(prev.Value) { etype = Change // Last condition assumes the fact is a duplicate. } else { continue } // Construct the event. event = &Event{ Type: etype, Entity: fact.Entity, Attribute: fact.Attribute, Before: prev, After: next, } events = append(events, event) } if err = iter.Err(); err != nil { return nil, err } // TODO: The final set of facts represent the current state // (ascending) or initial state (descending) of the iterator. return events, nil }