blob: ae412c66cd764d2938799726130265e3b6145760 [file] [log] [blame]
package cmd
import (
"github.com/Masterminds/cookoo"
"github.com/kylelemons/go-gypsy/yaml"
"fmt"
)
// ParseYaml parses the glide.yaml format and returns a Configuration object.
//
// Params:
// - filename (string): YAML filename as a string
//
// Context:
// - yaml.File: This puts the parsed YAML file into the context.
//
// Returns:
// - *Config: The configuration.
func ParseYaml(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
fname := p.Get("filename", "glide.yaml").(string)
conf := new(Config)
f, err := yaml.ReadFile(fname)
if err != nil {
return nil, err
}
c.Put("yaml.File", f)
// Convenience:
top, ok := f.Root.(yaml.Map)
if !ok {
return nil, fmt.Errorf("Expected YAML root to be map, got %t", f.Root)
}
vals := map[string]yaml.Node(top)
if name, ok := vals["package"]; ok {
//c.Put("cfg.package", name.(yaml.Scalar).String())
conf.Name = name.(yaml.Scalar).String()
} else {
Warn("The 'package' directive is required in Glide YAML.\n")
}
// Allow the user to override the behavior of `glide in`.
if incmd, ok := vals["incmd"]; ok {
conf.InCommand = incmd.(yaml.Scalar).String()
}
conf.Imports = make([]*Dependency, 0, 1)
if imp, ok := vals["import"]; ok {
imports, ok := imp.(yaml.List)
if ok {
for _, v := range imports {
pkg := v.(yaml.Map)
dep := Dependency {
Name: valOrEmpty("package", pkg),
Reference: valOrEmpty("ref", pkg),
VcsType: getVcsType(pkg),
Repository: valOrEmpty("repo", pkg),
Subpackages: subpkg("subpackages", pkg),
}
conf.Imports = append(conf.Imports, &dep)
}
}
}
return conf, nil
}
// WriteYaml writes a yaml.Node to the console as a string.
//
// Params:
// - yaml.Node: A yaml.Node to render.
func WriteYaml(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
top := p.Get("yaml.Node", yaml.Scalar("nothing to print")).(yaml.Node)
fmt.Print(yaml.Render(top))
return true, nil
}
// Convert a Config object and a yaml.File to a single yaml.File.
func MergeToYaml(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
root := c.Get("yaml.File", nil).(*yaml.File).Root
cfg := p.Get("conf", nil).(*Config)
rootMap, ok := root.(yaml.Map)
if !ok {
return nil, fmt.Errorf("Expected root node to be a map.")
}
rootMap["package"] = yaml.Scalar(cfg.Name)
if cfg.InCommand != "" {
rootMap["incmd"] = yaml.Scalar(cfg.InCommand)
}
// Imports
imports := make([]yaml.Node, len(cfg.Imports))
for i, imp := range cfg.Imports {
if imp.VcsType == NoVCS {
imp.VcsType, _ = GuessVCS(imp)
}
impmap := make(map[string]yaml.Node)
impmap["package"] = yaml.Scalar(imp.Name)
if imp.VcsType != NoVCS {
impmap["vcs"] = yaml.Scalar(vcsString(imp.VcsType))
}
if imp.Reference != "" {
impmap["ref"] = yaml.Scalar(imp.Reference)
}
if imp.Repository != "" {
impmap["repo"] = yaml.Scalar(imp.Repository)
}
if len(imp.Subpackages) > 0 {
subs := make([]yaml.Node, len(imp.Subpackages))
for ii, sub := range imp.Subpackages {
subs[ii] = yaml.Scalar(sub)
}
impmap["subpackages"] = yaml.List(subs)
}
imports[i] = yaml.Map(impmap)
}
rootMap["import"] = yaml.List(imports)
return root, nil
}
// AddDependencies adds a list of *Dependency objects to the given *Config.
//
// This is used to merge in packages from other sources or config files.
func AddDependencies(c cookoo.Context, p *cookoo.Params) (interface{}, cookoo.Interrupt) {
deps := p.Get("dependencies", []*Dependency{}).([]*Dependency)
config := p.Get("conf", nil).(*Config)
// Make a set of existing package names for quick comparison.
pkgSet := make(map[string]bool, len(config.Imports))
for _, p := range config.Imports {
pkgSet[p.Name] = true
}
// If a dep is not already present, add it.
for _, dep := range deps {
if _, ok := pkgSet[dep.Name]; ok {
Warn("Package %s is already in glide.yaml. Skipping.\n", dep.Name)
continue
}
config.Imports = append(config.Imports, dep)
}
return true, nil
}
func vcsString(vtype uint) string {
switch vtype {
case Git:
return "git"
case Hg:
return "hg"
case Bzr:
return "bzr"
case Svn:
return "svn"
default:
return ""
}
}
func valOrEmpty(key string, store map[string]yaml.Node) string {
val, ok := store[key]
if !ok {
return ""
}
return val.(yaml.Scalar).String()
}
func subpkg(key string, store map[string]yaml.Node) []string {
val, ok := store[key]
subpackages := []string{}
if !ok {
return subpackages
}
pkgs, ok := val.(yaml.List)
if !ok {
// Special case: Allow 'subpackages: justOne'
if one, ok := val.(yaml.Scalar); ok {
return []string{ one.String() }
}
Warn("Expected list of subpackages.\n")
return subpackages
}
for _, pkg := range pkgs {
subpackages = append(subpackages, pkg.(yaml.Scalar).String())
}
return subpackages
}
func getVcsType(store map[string]yaml.Node) uint {
val, ok := store["vcs"]
if !ok {
return NoVCS
}
name := val.(yaml.Scalar).String()
switch name {
case "git":
return Git
case "hg", "mercurial":
return Hg
case "bzr", "bazaar":
return Bzr
case "svn", "subversion":
return Svn
default:
return NoVCS
}
}
// Config is the top-level configuration object.
type Config struct {
Name string
Imports []*Dependency
DevImports []*Dependency
// InCommand is the default shell command run to start a 'glide in'
// session.
InCommand string
}
// Dependency describes a package that the present package depends upon.
type Dependency struct {
Name, Reference, Repository string
VcsType uint
Subpackages []string
}