gps/discovery.go

Summary

Maintainability
A
0 mins
Test Coverage
// Copyright 2012 The Go Authors.  All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package gps

// This code is taken from cmd/go/discovery.go; it is the logic go get itself
// uses to interpret meta imports information.

import (
    "encoding/xml"
    "fmt"
    "io"
    "strings"
)

// charsetReader returns a reader for the given charset. Currently
// it only supports UTF-8 and ASCII. Otherwise, it returns a meaningful
// error which is printed by go get, so the user can find why the package
// wasn't downloaded if the encoding is not supported. Note that, in
// order to reduce potential errors, ASCII is treated as UTF-8 (i.e. characters
// greater than 0x7f are not rejected).
func charsetReader(charset string, input io.Reader) (io.Reader, error) {
    switch strings.ToLower(charset) {
    case "ascii":
        return input, nil
    default:
        return nil, fmt.Errorf("can't decode XML document using charset %q", charset)
    }
}

type metaImport struct {
    Prefix, VCS, RepoRoot string
}

// parseMetaGoImports returns meta imports from the HTML in r.
// Parsing ends at the end of the <head> section or the beginning of the <body>.
//
// This copy of cmd/go/internal/vcs.parseMetaGoImports always operates
// in IgnoreMod ModuleMode.
func parseMetaGoImports(r io.Reader) (imports []metaImport, err error) {
    d := xml.NewDecoder(r)
    d.CharsetReader = charsetReader
    d.Strict = false
    var t xml.Token
    for {
        t, err = d.RawToken()
        if err != nil {
            if err == io.EOF || len(imports) > 0 {
                err = nil
            }
            return
        }
        if e, ok := t.(xml.StartElement); ok && strings.EqualFold(e.Name.Local, "body") {
            return
        }
        if e, ok := t.(xml.EndElement); ok && strings.EqualFold(e.Name.Local, "head") {
            return
        }
        e, ok := t.(xml.StartElement)
        if !ok || !strings.EqualFold(e.Name.Local, "meta") {
            continue
        }
        if attrValue(e.Attr, "name") != "go-import" {
            continue
        }
        if f := strings.Fields(attrValue(e.Attr, "content")); len(f) == 3 {
            // Ignore VCS type "mod", which is applicable only in module mode.
            if f[1] == "mod" {
                continue
            }
            imports = append(imports, metaImport{
                Prefix:   f[0],
                VCS:      f[1],
                RepoRoot: f[2],
            })
        }
    }
}

// attrValue returns the attribute value for the case-insensitive key
// `name', or the empty string if nothing is found.
func attrValue(attrs []xml.Attr, name string) string {
    for _, a := range attrs {
        if strings.EqualFold(a.Name.Local, name) {
            return a.Value
        }
    }
    return ""
}