|  | package vendor | 
|  |  | 
|  | import ( | 
|  | "bytes" | 
|  | "encoding/json" | 
|  | "fmt" | 
|  | "io" | 
|  | "os" | 
|  | "reflect" | 
|  | "sort" | 
|  | ) | 
|  |  | 
|  | // gb-vendor manifest support | 
|  |  | 
|  | // Manifest describes the layout of $PROJECT/vendor/manifest. | 
|  | type Manifest struct { | 
|  | // Manifest version. Current manifest version is 0. | 
|  | Version int `json:"version"` | 
|  |  | 
|  | // Depenencies is a list of vendored dependencies. | 
|  | Dependencies []Dependency `json:"dependencies"` | 
|  | } | 
|  |  | 
|  | // AddDependency adds a Dependency to the current Manifest. | 
|  | // If the dependency exists already then it returns and error. | 
|  | func (m *Manifest) AddDependency(dep Dependency) error { | 
|  | if m.HasImportpath(dep.Importpath) { | 
|  | return fmt.Errorf("already registered") | 
|  | } | 
|  | m.Dependencies = append(m.Dependencies, dep) | 
|  | return nil | 
|  | } | 
|  |  | 
|  | // RemoveDependency removes a Dependency from the current Manifest. | 
|  | // If the dependency does not exist then it returns an error. | 
|  | func (m *Manifest) RemoveDependency(dep Dependency) error { | 
|  | for i, d := range m.Dependencies { | 
|  | if reflect.DeepEqual(d, dep) { | 
|  | m.Dependencies = append(m.Dependencies[:i], m.Dependencies[i+1:]...) | 
|  | return nil | 
|  | } | 
|  | } | 
|  | return fmt.Errorf("dependency does not exist") | 
|  | } | 
|  |  | 
|  | // HasImportpath reports whether the Manifest contains the import path. | 
|  | func (m *Manifest) HasImportpath(path string) bool { | 
|  | _, err := m.GetDependencyForImportpath(path) | 
|  | return err == nil | 
|  | } | 
|  |  | 
|  | // GetDependencyForRepository return a dependency for specified URL | 
|  | // If the dependency does not exist it returns an error | 
|  | func (m *Manifest) GetDependencyForImportpath(path string) (Dependency, error) { | 
|  | for _, d := range m.Dependencies { | 
|  | if d.Importpath == path { | 
|  | return d, nil | 
|  | } | 
|  | } | 
|  | return Dependency{}, fmt.Errorf("dependency for %s does not exist", path) | 
|  | } | 
|  |  | 
|  | // Dependency describes one vendored import path of code | 
|  | // A Dependency is an Importpath sources from a Respository | 
|  | // at Revision from Path. | 
|  | type Dependency struct { | 
|  | // Importpath is name by which this dependency is known. | 
|  | Importpath string `json:"importpath"` | 
|  |  | 
|  | // Repository is the remote DVCS location that this | 
|  | // dependency was fetched from. | 
|  | Repository string `json:"repository"` | 
|  |  | 
|  | // Revision is the revision that describes the dependency's | 
|  | // remote revision. | 
|  | Revision string `json:"revision"` | 
|  |  | 
|  | // Branch is the branch the Revision was located on. | 
|  | // Can be blank if not needed. | 
|  | Branch string `json:"branch"` | 
|  |  | 
|  | // Path is the path inside the Repository where the | 
|  | // dependency was fetched from. | 
|  | Path string `json:"path,omitempty"` | 
|  | } | 
|  |  | 
|  | // WriteManifest writes a Manifest to the path. If the manifest does | 
|  | // not exist, it is created. If it does exist, it will be overwritten. | 
|  | // If the manifest file is empty (0 dependencies) it will be deleted. | 
|  | // The dependencies will be ordered by import path to reduce churn when making | 
|  | // changes. | 
|  | // TODO(dfc) write to temporary file and move atomically to avoid | 
|  | // destroying a working vendorfile. | 
|  | func WriteManifest(path string, m *Manifest) error { | 
|  | if len(m.Dependencies) == 0 { | 
|  | err := os.Remove(path) | 
|  | if !os.IsNotExist(err) { | 
|  | return err | 
|  | } | 
|  | return nil | 
|  | } | 
|  |  | 
|  | f, err := os.Create(path) | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | if err := writeManifest(f, m); err != nil { | 
|  | f.Close() | 
|  | return err | 
|  | } | 
|  | return f.Close() | 
|  | } | 
|  |  | 
|  | func writeManifest(w io.Writer, m *Manifest) error { | 
|  | sort.Sort(byImportpath(m.Dependencies)) | 
|  | buf, err := json.MarshalIndent(m, "", "\t") | 
|  | if err != nil { | 
|  | return err | 
|  | } | 
|  | _, err = io.Copy(w, bytes.NewReader(buf)) | 
|  | return err | 
|  | } | 
|  |  | 
|  | // ReadManifest reads a Manifest from path. If the Manifest is not | 
|  | // found, a blank Manifest will be returned. | 
|  | func ReadManifest(path string) (*Manifest, error) { | 
|  | f, err := os.Open(path) | 
|  | if err != nil { | 
|  | if os.IsNotExist(err) { | 
|  | return new(Manifest), nil | 
|  | } | 
|  | return nil, err | 
|  | } | 
|  | defer f.Close() | 
|  | return readManifest(f) | 
|  | } | 
|  |  | 
|  | func readManifest(r io.Reader) (*Manifest, error) { | 
|  | var m Manifest | 
|  | d := json.NewDecoder(r) | 
|  | err := d.Decode(&m) | 
|  | return &m, err | 
|  | } | 
|  |  | 
|  | type byImportpath []Dependency | 
|  |  | 
|  | func (s byImportpath) Len() int           { return len(s) } | 
|  | func (s byImportpath) Less(i, j int) bool { return s[i].Importpath < s[j].Importpath } | 
|  | func (s byImportpath) Swap(i, j int)      { s[i], s[j] = s[j], s[i] } |