github.com/hugh712/snapd@v0.0.0-20200910133618-1a99902bd583/snap/naming/snapref.go (about)

     1  // -*- Mode: Go; indent-tabs-mode: t -*-
     2  
     3  /*
     4   * Copyright (C) 2020 Canonical Ltd
     5   *
     6   * This program is free software: you can redistribute it and/or modify
     7   * it under the terms of the GNU General Public License version 3 as
     8   * published by the Free Software Foundation.
     9   *
    10   * This program is distributed in the hope that it will be useful,
    11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
    12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13   * GNU General Public License for more details.
    14   *
    15   * You should have received a copy of the GNU General Public License
    16   * along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17   *
    18   */
    19  
    20  package naming
    21  
    22  // A SnapRef references a snap by name and/or id.
    23  type SnapRef interface {
    24  	SnapName() string
    25  	ID() string
    26  }
    27  
    28  // Snap references a snap by name only.
    29  type Snap string
    30  
    31  func (s Snap) SnapName() string {
    32  	return string(s)
    33  }
    34  
    35  func (s Snap) ID() string {
    36  	return ""
    37  }
    38  
    39  type snapRef struct {
    40  	name string
    41  	id   string
    42  }
    43  
    44  // NewSnapRef returns a reference to the snap with given name and id.
    45  func NewSnapRef(name, id string) SnapRef {
    46  	return &snapRef{name: name, id: id}
    47  }
    48  
    49  func (r *snapRef) SnapName() string {
    50  	return r.name
    51  }
    52  
    53  func (r *snapRef) ID() string {
    54  	return r.id
    55  }
    56  
    57  // SameSnap returns whether the two arguments refer to the same snap.
    58  // If ids are not available for both it will fallback to names.
    59  func SameSnap(snapRef1, snapRef2 SnapRef) bool {
    60  	id1 := snapRef1.ID()
    61  	id2 := snapRef2.ID()
    62  	if id1 != "" && id2 != "" {
    63  		return id1 == id2
    64  	}
    65  	return snapRef1.SnapName() == snapRef2.SnapName()
    66  }
    67  
    68  // SnapSet can hold a set of references to snaps.
    69  type SnapSet struct {
    70  	byID   map[string]SnapRef
    71  	byName map[string]SnapRef
    72  
    73  	n int
    74  }
    75  
    76  // NewSnapSet builds a snap set with the given references.
    77  func NewSnapSet(refs []SnapRef) *SnapSet {
    78  	sz := len(refs) + 2
    79  	s := &SnapSet{
    80  		byID:   make(map[string]SnapRef, sz),
    81  		byName: make(map[string]SnapRef, sz),
    82  	}
    83  	for _, r := range refs {
    84  		s.Add(r)
    85  	}
    86  	return s
    87  }
    88  
    89  // Empty returns whether the snap set is empty.
    90  func (s *SnapSet) Empty() bool {
    91  	return s.n == 0
    92  }
    93  
    94  // Size returns the number of snaps in the snap set.
    95  func (s *SnapSet) Size() int {
    96  	return s.n
    97  }
    98  
    99  // Lookup finds the reference in the set matching the given one if any.
   100  func (s *SnapSet) Lookup(which SnapRef) SnapRef {
   101  	whichID := which.ID()
   102  	name := which.SnapName()
   103  	if whichID != "" {
   104  		if ref := s.byID[whichID]; ref != nil {
   105  			return ref
   106  		}
   107  	}
   108  	ref := s.byName[name]
   109  	if ref == nil || (ref.ID() != "" && whichID != "") {
   110  		return nil
   111  	}
   112  	return ref
   113  }
   114  
   115  // Contains returns whether the set has a matching reference already.
   116  func (s *SnapSet) Contains(ref SnapRef) bool {
   117  	return s.Lookup(ref) != nil
   118  }
   119  
   120  // Add adds one reference to the set.
   121  // Already added ids or names will be ignored. The assumption is that
   122  // a SnapSet is populated with distinct snaps.
   123  func (s *SnapSet) Add(ref SnapRef) {
   124  	if s.Contains(ref) {
   125  		// nothing to do
   126  		return
   127  	}
   128  	inc := 0
   129  	if id := ref.ID(); id != "" {
   130  		s.byID[id] = ref
   131  		inc = 1
   132  	}
   133  	if name := ref.SnapName(); name != "" {
   134  		s.byName[name] = ref
   135  		inc = 1
   136  	}
   137  	s.n += inc
   138  }