match on package substring

This allows godoc.org to work on nested packages. As it stood gddo would
fail trying to find nested packages. Let's say we have a vaind with this
route:

go.mcquay.me/vain -> https://s.mcquay.me/sm/vain

Asking godoc for go.mcquay.me/vain would work fine. However trying to
get documentation for go.mcquay.me/vain/errors (nested package) would
return 404 since it wouldn't match known paths exactly.

Since now we match and return "go.mcquay.me/vain" for the full path,
gddo is able to use local caches for its information.

Change-Id: I599a75898493734fc652e507f477c11b1b1b13e8
This commit is contained in:
Stephen McQuay 2016-06-28 21:17:23 -07:00
parent 073ad38bc0
commit 479ef2b786
No known key found for this signature in database
GPG Key ID: 1ABF428F71BAFC3D
2 changed files with 98 additions and 3 deletions

21
db.go
View File

@ -5,6 +5,7 @@ import (
"fmt" "fmt"
"net/http" "net/http"
"os" "os"
"strings"
"sync" "sync"
"time" "time"
@ -75,16 +76,30 @@ func (m *MemDB) NSForToken(ns namespace, tok Token) error {
// Package fetches the package associated with path. // Package fetches the package associated with path.
func (m *MemDB) Package(pth string) (Package, error) { func (m *MemDB) Package(pth string) (Package, error) {
m.l.RLock() m.l.RLock()
defer m.l.RUnlock()
pkg, ok := m.Packages[path(pth)] pkg, ok := m.Packages[path(pth)]
m.l.RUnlock() if ok {
return pkg, nil
}
var longest Package
for _, p := range m.Packages {
if splitPathHasPrefix(strings.Split(pth, "/"), strings.Split(p.Path, "/")) {
if len(p.Path) > len(longest.Path) {
longest = p
}
}
}
var err error var err error
if !ok { if longest.Path == "" {
err = verrors.HTTP{ err = verrors.HTTP{
Message: fmt.Sprintf("couldn't find package %q", pth), Message: fmt.Sprintf("couldn't find package %q", pth),
Code: http.StatusNotFound, Code: http.StatusNotFound,
} }
} }
return pkg, err return longest, err
} }
// AddPackage adds p into packages table. // AddPackage adds p into packages table.

80
db_test.go Normal file
View File

@ -0,0 +1,80 @@
package vain
import (
"errors"
"testing"
)
func TestPartialPackage(t *testing.T) {
db, done := TestDB(t)
if db == nil {
t.Fatalf("could not create temp db")
}
defer done()
paths := []path{
"a/b",
"a/c",
"a/d/c",
"a/d/e",
"f/b/c/d",
"f/b/c/e",
}
for _, p := range paths {
db.Packages[p] = Package{Path: string(p)}
}
tests := []struct {
pth string
pkg Package
err error
}{
// obvious
{
pth: "a/b",
pkg: db.Packages["a/b"],
},
{
pth: "a/d/c",
pkg: db.Packages["a/d/c"],
},
// here we exercise the code that matches closest submatch
{
pth: "a/b/c",
pkg: db.Packages["a/b"],
},
{
pth: "f/b/c/d/e/f/g",
pkg: db.Packages["f/b/c/d"],
},
// some errors
{
pth: "foo",
err: errors.New("shouldn't find"),
},
{
pth: "a/d/f",
err: errors.New("shouldn't find"),
},
}
for _, test := range tests {
p, err := db.Package(test.pth)
if got, want := p, test.pkg; got != want {
t.Errorf("bad package fetched: got %+v, want %+v", got, want)
}
got := err
want := test.err
if (got == nil) != (want == nil) {
t.Errorf("unexpected error; got %v, want %v", got, want)
}
}
}