@@ -443,6 +443,76 @@ def build_repo_download_url(purl):
443443 return get_repo_download_url (purl )
444444
445445
446+ @download_router .route ("pkg:hex/.*" )
447+ def build_hex_download_url (purl ):
448+ """
449+ Return a hex download URL from the `purl` string.
450+ """
451+ purl_data = PackageURL .from_string (purl )
452+
453+ name = purl_data .name
454+ version = purl_data .version
455+
456+ if name and version :
457+ return f"https://repo.hex.pm/tarballs/{ name } -{ version } .tar"
458+
459+
460+ @download_router .route ("pkg:golang/.*" )
461+ def build_golang_download_url (purl ):
462+ """
463+ Return a golang download URL from the `purl` string.
464+ """
465+ purl_data = PackageURL .from_string (purl )
466+
467+ namespace = purl_data .namespace
468+ name = purl_data .name
469+ version = purl_data .version
470+
471+ if not name :
472+ return
473+
474+ # TODO: https://github.com/package-url/packageurl-python/issues/197
475+ if namespace :
476+ name = f"{ namespace } /{ name } "
477+
478+ ename = escape_golang_path (name )
479+ eversion = escape_golang_path (version )
480+
481+ if name and version :
482+ return f"https://proxy.golang.org/{ ename } /@v/{ eversion } .zip"
483+
484+
485+ @download_router .route ("pkg:pub/.*" )
486+ def build_pub_download_url (purl ):
487+ """
488+ Return a pub download URL from the `purl` string.
489+ """
490+ purl_data = PackageURL .from_string (purl )
491+
492+ name = purl_data .name
493+ version = purl_data .version
494+
495+ if name and version :
496+ return f"https://pub.dev/api/archives/{ name } -{ version } .tar.gz"
497+
498+
499+ @download_router .route ("pkg:swift/.*" )
500+ def build_swift_download_url (purl ):
501+ """
502+ Return a Swift Package download URL from the `purl` string.
503+ """
504+ purl_data = PackageURL .from_string (purl )
505+
506+ name = purl_data .name
507+ version = purl_data .version
508+ namespace = purl_data .namespace
509+
510+ if not (namespace or name or version ):
511+ return
512+
513+ return f"https://{ namespace } /{ name } /archive/{ version } .zip"
514+
515+
446516def get_repo_download_url (purl ):
447517 """
448518 Return ``download_url`` if present in ``purl`` qualifiers or
@@ -470,3 +540,24 @@ def get_repo_download_url(purl):
470540 return get_repo_download_url_by_package_type (
471541 type = type , namespace = namespace , name = name , version = version
472542 )
543+
544+
545+ # TODO: https://github.com/package-url/packageurl-python/issues/196
546+ def escape_golang_path (path : str ) -> str :
547+ """
548+ Return an case-encoded module path or version name.
549+
550+ This is done by replacing every uppercase letter with an exclamation mark followed by the
551+ corresponding lower-case letter, in order to avoid ambiguity when serving from case-insensitive
552+ file systems.
553+
554+ See https://golang.org/ref/mod#goproxy-protocol.
555+ """
556+ escaped_path = ""
557+ for c in path :
558+ if c >= "A" and c <= "Z" :
559+ # replace uppercase with !lowercase
560+ escaped_path += "!" + chr (ord (c ) + ord ("a" ) - ord ("A" ))
561+ else :
562+ escaped_path += c
563+ return escaped_path
0 commit comments