Home Download Docs Code Community
     1	package index
     2	
     3	import (
     4		"context"
     5		"fmt"
     6		"os"
     7		"strconv"
     8		"time"
     9	
    10		"perkeep.org/pkg/blob"
    11		"perkeep.org/pkg/schema/nodeattr"
    12		"perkeep.org/pkg/types/camtypes"
    13	)
    14	
    15	// LocationHelper queries permanode locations.
    16	//
    17	// A LocationHelper is not safe for concurrent use.
    18	// Callers should use Lock or RLock on the underlying index instead.
    19	type LocationHelper struct {
    20		index  *Index
    21		corpus *Corpus // may be nil
    22	}
    23	
    24	// NewLocationHelper returns a new location handler
    25	// that uses ix to query blob attributes.
    26	func NewLocationHelper(ix *Index) *LocationHelper {
    27		lh := &LocationHelper{index: ix}
    28		if ix.corpus != nil {
    29			lh.corpus = ix.corpus
    30		}
    31		return lh
    32	}
    33	
    34	// SetCorpus sets the corpus to be used
    35	// for location lookups.
    36	func (lh *LocationHelper) SetCorpus(corpus *Corpus) {
    37		lh.corpus = corpus
    38	}
    39	
    40	// altLocationRef maps camliNodeType to a slice of attributes
    41	// whose values may refer to permanodes with location information.
    42	var altLocationRef = map[string][]string{
    43		// TODO(mpl): twitter.
    44		"foursquare.com:checkin": {"foursquareVenuePermanode"},
    45	}
    46	
    47	// PermanodeLocation returns the location info for a permanode,
    48	// from one of the following sources:
    49	//  1. Permanode attributes "latitude" and "longitude"
    50	//  2. Referenced permanode attributes (eg. for "foursquare.com:checkin"
    51	//     its "foursquareVenuePermanode")
    52	//  3. Location in permanode camliContent file metadata
    53	//
    54	// The sources are checked in this order, the location from
    55	// the first source yielding a valid result is returned.
    56	func (lh *LocationHelper) PermanodeLocation(ctx context.Context, permaNode blob.Ref,
    57		at time.Time, owner *Owner) (camtypes.Location, error) {
    58		return lh.permanodeLocation(ctx, permaNode, at, owner, true)
    59	}
    60	
    61	func (lh *LocationHelper) permanodeLocation(ctx context.Context,
    62		pn blob.Ref, at time.Time, owner *Owner,
    63		useRef bool) (loc camtypes.Location, err error) {
    64	
    65		signerID := owner.KeyID() // might be empty
    66		pa := permAttr{at: at, signerFilter: owner.RefSet(signerID)}
    67		if lh.corpus != nil {
    68			var claims []*camtypes.Claim
    69			pa.attrs, claims = lh.corpus.permanodeAttrsOrClaims(pn, at, signerID)
    70			if claims != nil {
    71				pa.claims = claimPtrSlice(claims)
    72			}
    73		} else {
    74			var claims []camtypes.Claim
    75			claims, err = lh.index.AppendClaims(ctx, nil, pn, signerID, "")
    76			if err != nil {
    77				return camtypes.Location{}, err
    78			}
    79			pa.claims = claimSlice(claims)
    80		}
    81	
    82		// Rule 1: if permanode has an explicit latitude and longitude,
    83		// then this is its location.
    84		slat, slong := pa.get(nodeattr.Latitude), pa.get(nodeattr.Longitude)
    85		if slat != "" && slong != "" {
    86			lat, latErr := strconv.ParseFloat(slat, 64)
    87			long, longErr := strconv.ParseFloat(slong, 64)
    88			switch {
    89			case latErr != nil:
    90				err = fmt.Errorf("invalid latitude in %v: %v", pn, latErr)
    91			case longErr != nil:
    92				err = fmt.Errorf("invalid longitude in %v: %v", pn, longErr)
    93			default:
    94				err = nil
    95			}
    96			return camtypes.Location{Latitude: lat, Longitude: long}, err
    97		}
    98	
    99		if useRef {
   100			// Rule 2: referenced permanode attributes
   101			nodeType := pa.get(nodeattr.Type)
   102			if nodeType != "" {
   103				for _, a := range altLocationRef[nodeType] {
   104					refPn, hasRef := blob.Parse(pa.get(a))
   105					if !hasRef {
   106						continue
   107					}
   108					loc, err = lh.permanodeLocation(ctx, refPn, at, owner, false)
   109					if err == nil {
   110						return loc, err
   111					}
   112				}
   113			}
   114	
   115			// Rule 3: location in permanode camliContent file metadata.
   116			// Use this only if pn was the argument passed to sh.getPermanodeLocation,
   117			// and is not something found through a reference via altLocationRef.
   118			if content, ok := blob.Parse(pa.get(nodeattr.CamliContent)); ok {
   119				return lh.index.GetFileLocation(ctx, content)
   120			}
   121		}
   122	
   123		return camtypes.Location{}, os.ErrNotExist
   124	}
   125	
   126	// permAttr represents attributes of a permanode
   127	// for a given owner at a given time, either
   128	// from a Corpus or Index/Interface.
   129	type permAttr struct {
   130		// permanode attributes from Corpus.PermanodeAttrs
   131		// This may be nil if corpus has no cache for the permanode
   132		// for the given time, in that case claims must be used.
   133		attrs map[string][]string
   134	
   135		// claims of permanode
   136		// Populated only when attrs is not valid; using AppendClaims
   137		// of corpus, or of the index in the absence of a corpus.
   138		// Both attrs and claims may be nil if the permanode
   139		// does not exist, or all attributes of it are from
   140		// signer(s) other than the signerFilter.
   141		claims claimsIntf
   142	
   143		at           time.Time
   144		signerFilter SignerRefSet
   145	}
   146	
   147	// get returns the value of attr.
   148	func (pa permAttr) get(attr string) string {
   149		if pa.attrs != nil {
   150			v := pa.attrs[attr]
   151			if len(v) != 0 {
   152				return v[0]
   153			}
   154			return ""
   155		}
   156	
   157		if pa.claims != nil {
   158			return claimsIntfAttrValue(pa.claims, attr, pa.at, pa.signerFilter)
   159		}
   160	
   161		return ""
   162	}
Website layout inspired by memcached.
Content by the authors.