asteris-llc/converge

View on GitHub
render/preprocessor/preprocessor.go

Summary

Maintainability
A
1 hr
Test Coverage
// Copyright © 2016 Asteris, LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

package preprocessor

import (
    "strings"

    "github.com/asteris-llc/converge/parse"

    "github.com/asteris-llc/converge/graph"
    "github.com/asteris-llc/converge/resource/module"
)

// VertexSplit takes a graph with a set of vertexes and a string, and returns
// the longest vertex id from the graph and the remainder of the string.  If no
// matching vertex is found 'false' is returned.
func VertexSplit(g *graph.Graph, s string) (string, string, bool) {
    prefix, found := Find(Prefixes(s), g.Contains)
    if !found {
        return "", s, false
    }
    if prefix == s {
        return prefix, "", true
    }
    return prefix, s[len(prefix)+1:], true
}

// VertexSplitTraverse will act like vertex split, looking for a prefix matching
// the current set of graph nodes, however unlike `VertexSplit`, if a node is
// not found at the current level it will look at the parent level to the
// provided starting node, unless stop(parent) returns true.
func VertexSplitTraverse(g *graph.Graph, toFind string, startingNode string, stop func(*graph.Graph, string) bool, history map[string]struct{}) (string, string, bool) {
    history[startingNode] = struct{}{}

    for _, child := range g.Children(startingNode) {
        if _, ok := history[child]; ok {
            continue
        }
        if stop(g, child) {
            continue
        }
        vertex, middle, found := VertexSplitTraverse(g, toFind, child, stop, history)
        if found {
            return vertex, middle, found
        }
    }
    if stop(g, startingNode) {
        return "", toFind, false
    }

    fqgn := graph.SiblingID(startingNode, toFind)
    vertex, middle, found := VertexSplit(g, fqgn)
    if found {
        return vertex, middle, found
    }
    parentID := graph.ParentID(startingNode)
    return VertexSplitTraverse(g, toFind, parentID, stop, history)
}

// TraverseUntilModule is a function intended to be used with
// VertexSplitTraverse and will cause vertex splitting to propogate upwards
// until it encounters a module
func TraverseUntilModule(g *graph.Graph, id string) bool {
    if graph.IsRoot(id) {
        return true
    }
    elemMeta, ok := g.Get(id)
    if !ok {
        return true
    }
    elem := elemMeta.Value()
    if _, ok := elem.(*module.Module); ok {
        return true
    }
    if _, ok := elem.(*module.Preparer); ok {
        return true
    }
    if node, ok := elem.(*parse.Node); ok {
        return node.Kind() == "module"
    }
    return false
}

// Find returns the first element of the string slice for which f returns true
func Find(slice []string, f func(string) bool) (string, bool) {
    for _, elem := range slice {
        if f(elem) {
            return elem, true
        }
    }
    return "", false
}

// SplitTerms takes a string and splits it on '.'
func SplitTerms(in string) []string {
    return strings.Split(in, ".")
}

// JoinTerms takes a list of terms and joins them with '.'
func JoinTerms(s []string) string {
    return strings.Join(s, ".")
}

// Inits returns a list of heads of the string,
// e.g. [1,2,3] -> [[1,2,3],[1,2],[1]]
func Inits(in []string) [][]string {
    var results [][]string
    for i := 0; i < len(in); i++ {
        results = append([][]string{in[0 : i+1]}, results...)
    }
    return results
}

// Prefixes returns a set of prefixes for a string, e.g. "a.b.c.d" will yield
// []string{"a.b.c.d","a.b.c","a.b.","a"}
func Prefixes(in string) (out []string) {
    for _, termSet := range Inits(SplitTerms(in)) {
        out = append(out, JoinTerms(termSet))
    }
    return out
}