docker/swarmkit

View on GitHub
api/genericresource/parse.go

Summary

Maintainability
A
0 mins
Test Coverage
package genericresource

import (
    "encoding/csv"
    "fmt"
    "strconv"
    "strings"

    "github.com/moby/swarmkit/v2/api"
)

func newParseError(format string, args ...interface{}) error {
    return fmt.Errorf("could not parse GenericResource: "+format, args...)
}

// discreteResourceVal returns an int64 if the string is a discreteResource
// and an error if it isn't
func discreteResourceVal(res string) (int64, error) {
    return strconv.ParseInt(res, 10, 64)
}

// allNamedResources returns true if the array of resources are all namedResources
// e.g: res = [red, orange, green]
func allNamedResources(res []string) bool {
    for _, v := range res {
        if _, err := discreteResourceVal(v); err == nil {
            return false
        }
    }

    return true
}

// ParseCmd parses the Generic Resource command line argument
// and returns a list of *api.GenericResource
func ParseCmd(cmd string) ([]*api.GenericResource, error) {
    if strings.Contains(cmd, "\n") {
        return nil, newParseError("unexpected '\\n' character")
    }

    r := csv.NewReader(strings.NewReader(cmd))
    records, err := r.ReadAll()

    if err != nil {
        return nil, newParseError("%v", err)
    }

    if len(records) != 1 {
        return nil, newParseError("found multiple records while parsing cmd %v", records)
    }

    return Parse(records[0])
}

// Parse parses a table of GenericResource resources
func Parse(cmds []string) ([]*api.GenericResource, error) {
    tokens := make(map[string][]string)

    for _, term := range cmds {
        kva := strings.Split(term, "=")
        if len(kva) != 2 {
            return nil, newParseError("incorrect term %s, missing"+
                " '=' or malformed expression", term)
        }

        key := strings.TrimSpace(kva[0])
        val := strings.TrimSpace(kva[1])

        tokens[key] = append(tokens[key], val)
    }

    var rs []*api.GenericResource
    for k, v := range tokens {
        if u, ok := isDiscreteResource(v); ok {
            if u < 0 {
                return nil, newParseError("cannot ask for"+
                    " negative resource %s", k)
            }

            rs = append(rs, NewDiscrete(k, u))
            continue
        }

        if allNamedResources(v) {
            rs = append(rs, NewSet(k, v...)...)
            continue
        }

        return nil, newParseError("mixed discrete and named resources"+
            " in expression '%s=%s'", k, v)
    }

    return rs, nil
}

// isDiscreteResource returns true if the array of resources is a
// Discrete Resource.
// e.g: res = [1]
func isDiscreteResource(values []string) (int64, bool) {
    if len(values) != 1 {
        return int64(0), false
    }

    u, err := discreteResourceVal(values[0])
    if err != nil {
        return int64(0), false
    }

    return u, true

}