package bulk import ( "strings" "tangled.org/odd.computer/isomorph/internal/model" "tangled.org/odd.computer/isomorph/internal/repourl" ) // A project's website often stands for its repository (curl.se for github.com/curl/curl), but // the same site also hosts sibling projects: curl.se/trurl is trurl, sphinx-doc.org is the // homepage every sphinxcontrib-* package gives. So a candidate supported only by a project // site, rather than by a forge page or a source URL, decides a package only when the package // is named like the repository (docs/EVALUATION.md). // siteOnly reports whether every piece of evidence for key is a homepage on a site other than // the repository's own host. func siteOnly(evidence []model.Evidence, key string) bool { n := 0 for _, e := range evidence { if e.Key != key { continue } n++ if !isSiteHint(e, key) { return false } } return n > 0 } func isSiteHint(e model.Evidence, key string) bool { if BasisOf(e.Source) != BasisHomepage { return false } c, err := repourl.Parse(e.Value) if err == nil && c.Kind == repourl.KindRepo { return false // a forge page of the repository itself } return err != nil || c.Host != key[:strings.IndexByte(key+"/", '/')] } // flagshipNames are the package-name stems of flagship repositories whose name differs from // their packages'. var flagshipNames = map[string][]string{ "github.com/python/cpython": {"python", "cpython"}, "github.com/apache/httpd": {"apache", "httpd"}, "git.gnupg.org/gnupg": {"gnupg", "gpg"}, "git.savannah.gnu.org/libc": {"glibc", "libc"}, "git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux": {"linux", "kernel"}, } var ecosystemPrefixes = []string{"python3-", "python-", "py3-", "py-", "lib32-", "mingw32-", "mingw64-", "perl-", "ruby-", "rubygem-", "golang-", "rust-", "nodejs-", "node-"} // namedLike reports whether pkg is named like the repository at key: its name (without an // ecosystem prefix) is the repository's name or a stem of it, followed by nothing, a version // or a separator. rust1.88, binutils-djgpp and llvm-toolchain-19 are; trurl, lziprecover and // sphinxcontrib-jsmath are not. func namedLike(pkg, key string) bool { name := strings.ToLower(pkg) for _, p := range ecosystemPrefixes { name = strings.TrimPrefix(name, p) } stems := flagshipNames[key] if stems == nil { repo := key[strings.LastIndexByte(key, '/')+1:] stems = []string{repo} for _, suf := range []string{"-project", "-portable", "-src"} { if s := strings.TrimSuffix(repo, suf); s != repo { stems = append(stems, s) } } } for _, s := range stems { if len(s) >= 2 && (startsAtBoundary(name, s) || len(name) >= 3 && startsAtBoundary(s, name)) { return true } } return false } // startsAtBoundary reports whether s is prefix followed by nothing or a non-letter. func startsAtBoundary(s, prefix string) bool { if !strings.HasPrefix(s, prefix) { return false } rest := s[len(prefix):] return rest == "" || !(rest[0] >= 'a' && rest[0] <= 'z') }