diff options
| -rw-r--r-- | snap/naming/snapref.go | 129 | ||||
| -rw-r--r-- | snap/naming/snapref_test.go | 89 | ||||
| -rw-r--r-- | snap/naming/validate.go | 2 |
3 files changed, 219 insertions, 1 deletions
diff --git a/snap/naming/snapref.go b/snap/naming/snapref.go new file mode 100644 index 0000000000..60e2150ead --- /dev/null +++ b/snap/naming/snapref.go @@ -0,0 +1,129 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2019 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package naming + +// A SnapRef references a snap by name and/or id. +type SnapRef interface { + SnapName() string + ID() string +} + +// Snap references a snap by name only. +type Snap string + +func (s Snap) SnapName() string { + return string(s) +} + +func (s Snap) ID() string { + return "" +} + +type snapRef struct { + name string + id string +} + +// NewSnapRef returns a reference to the snap with given name and id. +func NewSnapRef(name, id string) SnapRef { + return &snapRef{name: name, id: id} +} + +func (r *snapRef) SnapName() string { + return r.name +} + +func (r *snapRef) ID() string { + return r.id +} + +// SameSnap returns whether the two arguments refer to the same snap. +// If ids are not available for both it will fallback to names. +func SameSnap(snapRef1, snapRef2 SnapRef) bool { + id1 := snapRef1.ID() + id2 := snapRef2.ID() + if id1 != "" && id2 != "" { + return id1 == id2 + } + return snapRef1.SnapName() == snapRef2.SnapName() +} + +// SnapSet can hold a set of references to snaps. +type SnapSet struct { + byID map[string]SnapRef + byNameWithID map[string]SnapRef + byNameOnly map[string]SnapRef +} + +// NewSnapSet builds a snap set with the given references. +func NewSnapSet(refs []SnapRef) *SnapSet { + s := &SnapSet{ + byID: make(map[string]SnapRef), + byNameWithID: make(map[string]SnapRef), + byNameOnly: make(map[string]SnapRef), + } + for _, r := range refs { + s.Add(r) + } + return s +} + +// Lookup finds the reference in the set matching the given one if any. +func (s *SnapSet) Lookup(which SnapRef) SnapRef { + whichID := which.ID() + name := which.SnapName() + if whichID != "" { + if ref := s.byID[whichID]; ref != nil { + return ref + } + } else { + if ref := s.byNameWithID[name]; ref != nil { + return ref + } + } + return s.byNameOnly[name] +} + +// Contains returns whether the set has a matching reference already. +func (s *SnapSet) Contains(ref SnapRef) bool { + return s.Lookup(ref) != nil +} + +// Add adds one reference to the set. +// Already added ids or names will be ignored. The assumption is that +// a SnapSet is populated with distinct snaps. +func (s *SnapSet) Add(ref SnapRef) { + if s.Contains(ref) { + // nothing to do + return + } + id := ref.ID() + if id != "" { + s.byID[id] = ref + } + name := ref.SnapName() + if name != "" { + if id != "" { + s.byNameWithID[name] = ref + } else { + s.byNameOnly[name] = ref + } + } +} diff --git a/snap/naming/snapref_test.go b/snap/naming/snapref_test.go new file mode 100644 index 0000000000..ceadde0301 --- /dev/null +++ b/snap/naming/snapref_test.go @@ -0,0 +1,89 @@ +// -*- Mode: Go; indent-tabs-mode: t -*- + +/* + * Copyright (C) 2019 Canonical Ltd + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + * + */ + +package naming_test + +import ( + . "gopkg.in/check.v1" + + "github.com/snapcore/snapd/snap/naming" +) + +type snapRefSuite struct{} + +var _ = Suite(&snapRefSuite{}) + +func (s *snapRefSuite) TestNewSnapRef(c *C) { + fooRef := naming.NewSnapRef("foo", "foo-id") + c.Check(fooRef.SnapName(), Equals, "foo") + c.Check(fooRef.ID(), Equals, "foo-id") + + fooNameOnlyRef := naming.NewSnapRef("foo", "") + c.Check(fooNameOnlyRef.SnapName(), Equals, "foo") + c.Check(fooNameOnlyRef.ID(), Equals, "") +} + +func (s *snapRefSuite) TestSnap(c *C) { + fooNameOnlyRef := naming.Snap("foo") + c.Check(fooNameOnlyRef.SnapName(), Equals, "foo") + c.Check(fooNameOnlyRef.ID(), Equals, "") +} + +func (s *snapRefSuite) TestSameSnap(c *C) { + fooRef := naming.NewSnapRef("foo", "foo-id") + fooNameOnlyRef := naming.NewSnapRef("foo", "") + altFooRef := naming.NewSnapRef("foo-proj", "foo-id") + barNameOnylRef := naming.NewSnapRef("bar", "") + unrelFooRef := naming.NewSnapRef("foo", "unrel-id") + + c.Check(naming.SameSnap(fooRef, altFooRef), Equals, true) + c.Check(naming.SameSnap(fooRef, fooNameOnlyRef), Equals, true) + c.Check(naming.SameSnap(altFooRef, fooNameOnlyRef), Equals, false) + c.Check(naming.SameSnap(unrelFooRef, fooRef), Equals, false) + c.Check(naming.SameSnap(fooRef, barNameOnylRef), Equals, false) + // weakness but expected + c.Check(naming.SameSnap(unrelFooRef, fooNameOnlyRef), Equals, true) +} + +func (s *snapRefSuite) TestSnapSet(c *C) { + ss := naming.NewSnapSet(nil) + fooRef := naming.NewSnapRef("foo", "foo-id") + fooNameOnlyRef := naming.Snap("foo") + + ss.Add(fooRef) + ss.Add(fooNameOnlyRef) + + altFooRef := naming.NewSnapRef("foo-proj", "foo-id") + c.Check(ss.Lookup(fooRef), Equals, fooRef) + c.Check(ss.Lookup(fooNameOnlyRef), Equals, fooRef) + c.Check(ss.Lookup(altFooRef), Equals, fooRef) + + barNameOnylRef := naming.NewSnapRef("bar", "") + unrelFooRef := naming.NewSnapRef("foo", "unrel-id") + c.Check(ss.Lookup(barNameOnylRef), Equals, nil) + c.Check(ss.Lookup(unrelFooRef), Equals, nil) + + // weaker behavior but expected + ss1 := naming.NewSnapSet([]naming.SnapRef{fooNameOnlyRef}) + ss1.Add(fooRef) + c.Check(ss1.Lookup(fooRef), Equals, fooNameOnlyRef) + c.Check(ss1.Lookup(altFooRef), Equals, nil) + c.Check(ss1.Lookup(barNameOnylRef), Equals, nil) + c.Check(ss1.Lookup(unrelFooRef), Equals, fooNameOnlyRef) +} diff --git a/snap/naming/validate.go b/snap/naming/validate.go index 69d2299222..b739a8cb77 100644 --- a/snap/naming/validate.go +++ b/snap/naming/validate.go @@ -17,7 +17,7 @@ * */ -// Package naming implements naming constraints for snaps and their elements. +// Package naming implements naming constraints and concepts for snaps and their elements. package naming import ( |
