Skip to content

Commit 510fb62

Browse files
committed
Make newStorePathParts more robust, add TestCases
1 parent 94df293 commit 510fb62

File tree

2 files changed

+63
-10
lines changed

2 files changed

+63
-10
lines changed

internal/devpkg/package.go

Lines changed: 20 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ import (
1515
"regexp"
1616
"strings"
1717
"time"
18+
"unicode"
1819

1920
"github.com/pkg/errors"
2021
"github.com/samber/lo"
@@ -588,19 +589,28 @@ type storePathParts struct {
588589
version string
589590
}
590591

592+
// newStorePathParts splits a Nix store path into its hash, name and version
593+
// components in the same way that Nix does.
594+
//
595+
// See https://nixos.org/manual/nix/stable/language/builtins.html#builtins-parseDrvName
591596
func newStorePathParts(path string) *storePathParts {
592597
path = strings.TrimPrefix(path, "/nix/store/")
593598
// path is now <hash>-<name>-<version
594-
parts := strings.Split(path, "-")
595599

596-
// writing this a bit defensively to avoid edge-cases that may break
597-
var hash, name, version string
598-
hash = parts[0]
599-
if len(parts) > 1 {
600-
name = parts[1]
601-
}
602-
if len(parts) > 2 {
603-
version = parts[2]
600+
hash, name := path[:32], path[33:]
601+
dashIndex := 0
602+
for i, r := range name {
603+
if dashIndex != 0 && !unicode.IsLetter(r) {
604+
return &storePathParts{hash: hash, name: name[:dashIndex], version: name[i:]}
605+
}
606+
dashIndex = 0
607+
if r == '-' {
608+
dashIndex = i
609+
}
604610
}
605-
return &storePathParts{hash, name, version}
611+
return &storePathParts{hash, name, "" /*version*/}
612+
}
613+
614+
func (p *storePathParts) Equal(other *storePathParts) bool {
615+
return p.hash == other.hash && p.name == other.name && p.version == other.version
606616
}

internal/devpkg/package_test.go

Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -183,3 +183,46 @@ func TestHashFromNixPkgsURL(t *testing.T) {
183183
}
184184
}
185185
}
186+
187+
func TestStorePathParts(t *testing.T) {
188+
testCases := []struct {
189+
storePath string
190+
expected *storePathParts
191+
}{
192+
// simple case:
193+
{
194+
storePath: "/nix/store/cvrn84c1hshv2wcds7n1rhydi6lacqns-gnumake-4.4.1",
195+
expected: &storePathParts{
196+
hash: "cvrn84c1hshv2wcds7n1rhydi6lacqns",
197+
name: "gnumake",
198+
version: "4.4.1",
199+
},
200+
},
201+
// the package name can have dashes:
202+
{
203+
storePath: "/nix/store/q2xdxsswjqmqcbax81pmazm367s7jzyb-cctools-binutils-darwin-wrapper-973.0.1",
204+
expected: &storePathParts{
205+
hash: "q2xdxsswjqmqcbax81pmazm367s7jzyb",
206+
name: "cctools-binutils-darwin-wrapper",
207+
version: "973.0.1",
208+
},
209+
},
210+
// version is optional. This is an artificial example I constructed
211+
{
212+
storePath: "/nix/store/gfxwrd5nggc68pjj3g3jhlldim9rpg0p-coreutils",
213+
expected: &storePathParts{
214+
hash: "gfxwrd5nggc68pjj3g3jhlldim9rpg0p",
215+
name: "coreutils",
216+
},
217+
},
218+
}
219+
220+
for _, testCase := range testCases {
221+
t.Run(testCase.storePath, func(t *testing.T) {
222+
parts := newStorePathParts(testCase.storePath)
223+
if !parts.Equal(testCase.expected) {
224+
t.Errorf("Expected %v, got %v", testCase.expected, parts)
225+
}
226+
})
227+
}
228+
}

0 commit comments

Comments
 (0)