blob: 3d9dc0487b0b93773ba9276290749ebf7308ced1 [file] [log] [blame]
// Copyright 2016 The LUCI Authors.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package dm
import (
"errors"
"sort"
"github.com/xtgo/set"
)
// Normalize sorts and uniq's attempt nums
func (a *AttemptList) Normalize() error {
for q, vals := range a.GetTo() {
if vals == nil {
a.To[q] = &AttemptList_Nums{}
} else {
if err := vals.Normalize(); err != nil {
return err
}
}
}
return nil
}
type revUint32Slice []uint32
func (u revUint32Slice) Len() int { return len(u) }
func (u revUint32Slice) Swap(i, j int) { u[i], u[j] = u[j], u[i] }
// Less intentionally reverses the comparison of i and j.
func (u revUint32Slice) Less(i, j int) bool { return u[j] < u[i] }
// Normalize sorts and uniq's attempt nums. If Nums equals [0], [] or nil,
// it implies all attempts for the quest and will be normalized to nil.
//
// It is an error for Nums to contain 0 as well as other numbers.
func (a *AttemptList_Nums) Normalize() error {
if len(a.Nums) == 0 || (len(a.Nums) == 1 && a.Nums[0] == 0) {
a.Nums = nil
return nil
}
slc := revUint32Slice(a.Nums)
sort.Sort(slc)
if a.Nums[len(a.Nums)-1] == 0 {
return errors.New("AttemptList.Nums contains 0 as well as other values")
}
a.Nums = a.Nums[:set.Uniq(slc)]
return nil
}
// NewAttemptList is a convenience method for making a normalized
// *AttemptList with a pre-normalized literal map of quest -> attempt nums.
//
// If the provided data is invalid, this method will panic.
func NewAttemptList(data map[string][]uint32) *AttemptList {
ret := &AttemptList{To: make(map[string]*AttemptList_Nums, len(data))}
for qst, atmpts := range data {
nums := &AttemptList_Nums{Nums: atmpts}
if err := nums.Normalize(); err != nil {
panic(err)
}
ret.To[qst] = nums
}
return ret
}
// AddAIDs adds the given Attempt_ID to the AttemptList
func (a *AttemptList) AddAIDs(aids ...*Attempt_ID) {
for _, aid := range aids {
if a.To == nil {
a.To = map[string]*AttemptList_Nums{}
}
atmptNums := a.To[aid.Quest]
if atmptNums == nil {
atmptNums = &AttemptList_Nums{}
a.To[aid.Quest] = atmptNums
}
atmptNums.Nums = append(atmptNums.Nums, aid.Id)
}
}
// Dup does a deep copy of this AttemptList.
func (a *AttemptList) Dup() *AttemptList {
ret := &AttemptList{}
for k, v := range a.To {
if ret.To == nil {
ret.To = make(map[string]*AttemptList_Nums, len(a.To))
}
vals := &AttemptList_Nums{Nums: make([]uint32, len(v.Nums))}
copy(vals.Nums, v.Nums)
ret.To[k] = vals
}
return ret
}
// UpdateWith updates this AttemptList with all the entries in the other
// AttemptList.
func (a *AttemptList) UpdateWith(o *AttemptList) {
for qid, atmpts := range o.To {
if curAtmpts, ok := a.To[qid]; !ok {
a.To[qid] = atmpts
} else {
curAtmpts.Nums = append(curAtmpts.Nums, atmpts.Nums...)
curAtmpts.Normalize()
}
}
}