code: func tagMapFromProperties(props ds.PropertySlice) (TagMap, error) {
  	tm := TagMap{}
  	lme := errors.NewLazyMultiError(len(props))
  	for idx, prop := range props {
  		v, ok := prop.Value().(string)
  		if !ok {
  			lme.Assign(idx, fmt.Errorf("property is not a string (%T)", prop.Value()))
  			continue
  		}
  		e, err := decodeKey(v)
  		if err != nil {
  			lme.Assign(idx, fmt.Errorf("failed to decode property (%q): %s", v, err))
  			continue
  		}
  
  		parts := strings.SplitN(e, "=", 2)
  		if len(parts) != 2 {
  			// This is a presence entry. Ignore.
  			continue
  		}
  		k, v := parts[0], parts[1]
  
  		if err := types.ValidateTag(k, v); err != nil {
  			lme.Assign(idx, fmt.Errorf("invalid tag %q: %s", parts[0], err))
  			continue
  		}
  		tm[k] = v
  	}
  
  	if len(tm) == 0 {
  		tm = nil
  	}
  	return tm, lme.Get()
  }
docs: // tagMapFromProperties converts a set of tag property objects into a TagMap.
  //
  // If an error occurs decoding a specific property, an errors.MultiError will be
  // returned alongside the successfully-decoded tags.