diff --git a/bindings/ud.go b/bindings/ud.go index 15b0e012a7ffaad5b290da54b832bcffdf39fa57..900e102d75621c76d110732fca3c4011dfd23b8b 100644 --- a/bindings/ud.go +++ b/bindings/ud.go @@ -8,6 +8,7 @@ package bindings import ( + "fmt" "github.com/pkg/errors" "gitlab.com/elixxir/client/ud" "gitlab.com/elixxir/crypto/contact" @@ -167,7 +168,7 @@ func (ud UserDiscovery) SearchSingle(f string, callback SingleSearchCallback, return ud.ud.Search([]fact.Fact{fObj}, cb, timeout) } -// SingleSearchCallback returns the result of a single search +// LookupCallback returns the result of a single lookup type LookupCallback interface { Callback(contact *Contact, error string) } @@ -200,3 +201,98 @@ func (ud UserDiscovery) Lookup(idBytes []byte, callback LookupCallback, return ud.ud.Lookup(uid, cb, timeout) } + +// MultiLookupCallback returns the result of many paralel lookups +type MultiLookupCallback interface { + Callback(Succeeded *ContactList, failed *IdList, errors string) +} + + +type lookupResponse struct{ + C contact.Contact + err error + index int + id *id.ID +} + +// MultiLookup Looks for the contact object associated with all given userIDs. +// The ids are the byte representation of an id stored in an IDList object. +// This will reject if that id is malformed or if the indexing on the IDList +// object is wrong. The MultiLookupCallback will return with all contacts +// returned within the timeout. +func (ud UserDiscovery) MultiLookup(ids *IdList, callback MultiLookupCallback, + timeoutMS int) error { + + idList := make([]*id.ID,0,ids.Len()) + + //extract all IDs from + for i:=0;i<ids.Len();i++{ + idBytes, err := ids.Get(i) + if err!=nil{ + return errors.WithMessagef(err, "Failed to get ID at index %d", i) + } + uid, err := id.Unmarshal(idBytes) + if err != nil { + return errors.WithMessagef(err, "Failed to lookup due to "+ + "malformed id at index %d", i) + } + idList = append(idList, uid) + } + + //make the channels for the requests + results := make(chan lookupResponse, len(idList)) + + timeout := time.Duration(timeoutMS) * time.Millisecond + + //loop through the IDs and send the lookup + for i := range idList{ + locali := i + localID := idList[locali] + cb := func(c contact.Contact, err error){ + results <- lookupResponse{ + C: c, + err: err, + index: locali, + id: localID, + } + } + + go func(){ + err := ud.ud.Lookup(localID, cb, timeout) + if err!=nil{ + results <- lookupResponse{ + C: contact.Contact{}, + err: errors.WithMessagef(err, "Failed to send lookup " + + "for user %s[%d]", localID, locali), + index: locali, + id: localID, + } + } + }() + } + + //run the result gathering in its own thread + go func(){ + returnedContactList := make([]contact.Contact,0,len(idList)) + failedIDList := make([]*id.ID,0,len(idList)) + var concatonatedErrs string + + //Get the responses and return + for numReturned := 0; numReturned<len(idList);numReturned++{ + response := <- results + if response.err==nil{ + returnedContactList = append(returnedContactList, response.C) + }else{ + failedIDList = append(failedIDList, response.id) + concatonatedErrs = concatonatedErrs + fmt.Sprintf("Error returned from " + + "send to %d [%d]:%+v\t", response.id, response.index, response.err) + } + } + + callback.Callback(&ContactList{list:returnedContactList}, &IdList{list:failedIDList}, concatonatedErrs) + }() + + + return nil +} +