package zoopb // this is becoming a standard format // todo: autogenerate this from the .proto file? import ( "fmt" "os" "sort" sync "sync" "time" timestamppb "google.golang.org/protobuf/types/known/timestamppb" ) // bad global lock until I figure out some other plan var lock sync.RWMutex type PackageIterator struct { sync.RWMutex packs []*Package index int } // NewPackageIterator initializes a new iterator. func NewPackageIterator(packs []*Package) *PackageIterator { return &PackageIterator{packs: packs} } // Scan moves to the next element and returns false if there are no more packs. func (it *PackageIterator) Scan() bool { if it.index >= len(it.packs) { return false } it.index++ return true } // Package returns the current repo. func (it *PackageIterator) Package() *Package { if it.packs[it.index-1] == nil { for i, d := range it.packs { fmt.Println("i =", i, d) } fmt.Println("len =", len(it.packs)) fmt.Println("repo == nil", it.index, it.index-1) os.Exit(-1) } return it.packs[it.index-1] } // Use Scan() in a loop, similar to a while loop // // for iterator.Scan() { // d := iterator.Package() // fmt.Println("Package UUID:", d.Uuid) // } func (r *Packages) All() *PackageIterator { repoPointers := r.selectAllPackages() iterator := NewPackageIterator(repoPointers) return iterator } func (r *Packages) SortByName() *PackageIterator { packs := r.selectAllPackages() sort.Sort(ByName(packs)) iterator := NewPackageIterator(packs) return iterator } // enforces no duplicate package names func (r *Packages) Append(newP *Package) bool { lock.Lock() defer lock.Unlock() for _, p := range r.Packages { if p.Name == newP.Name { return false } } r.Packages = append(r.Packages, newP) return true } // Update version and timestamp. // returns ok (ok == true if not found) func (r *Packages) Update(newP *Package) bool { lock.Lock() defer lock.Unlock() var found *Package for _, p := range r.Packages { if p.Name == newP.Name { found = p } } if found == nil { // r.Append(newP) // update here? return true } var changed bool = false if newP.Version != found.Version { changed = true found.Version = newP.Version } now := time.Now() found.Laststamp = timestamppb.New(now) return changed } // returns time.Duration since last Update() func (r *Package) Age(newP *Package) time.Duration { t := time.Since(r.Laststamp.AsTime()) return t } // find a package by name func (r *Packages) FindByName(name string) *Package { lock.RLock() defer lock.RUnlock() for _, p := range r.Packages { if p.Name == name { return p } } return nil } func (r *Packages) Len() int { lock.RLock() defer lock.RUnlock() return len(r.Packages) } type ByName []*Package func (a ByName) Len() int { return len(a) } func (a ByName) Less(i, j int) bool { return a[i].Name < a[j].Name } func (a ByName) Swap(i, j int) { a[i], a[j] = a[j], a[i] } // safely returns a slice of pointers to the Package protobufs func (r *Packages) selectAllPackages() []*Package { lock.RLock() defer lock.RUnlock() // Create a new slice to hold pointers to each Package var allPacks []*Package allPacks = make([]*Package, len(r.Packages)) for i, p := range r.Packages { allPacks[i] = p // Copy pointers for safe iteration } return allPacks } /* func (r *Packages) UnmergedPackageRepos() *PackageRepoIterator { repoPointers := r.selectUnmergedPackageRepos() sort.Sort(ByName(repoPointers)) iterator := NewPackageRepoIterator(repoPointers) return iterator } */ /* // this sort doesn't really work. I think it forgets to sort the last two // todo: sort this out. literally // SelectPackagePointers safely returns a slice of pointers to Package records. func (r *Packages) selectUnmergedPackages() []*PackageRow { r.RLock() defer r.RUnlock() // Create a new slice to hold pointers to each Package // repoPointers := make([]*Package, len(c.E.Packages)) var repoPointers []*PackageRow for _, repo := range me.allrepos { repoPointers = append(repoPointers, repo) // Copy pointers for safe iteration } return repoPointers } */