github.com/paketoio/libpak@v1.3.1/buildpack_plan.go (about)

     1  /*
     2   * Copyright 2018-2020 the original author or authors.
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *      https://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   */
    16  
    17  package libpak
    18  
    19  import (
    20  	"fmt"
    21  	"reflect"
    22  	"strings"
    23  
    24  	"github.com/buildpacks/libcnb"
    25  	"github.com/imdario/mergo"
    26  )
    27  
    28  // PlanEntryResolver provides functionality for resolving a Buildpack Plan Entry given a name.
    29  type PlanEntryResolver struct {
    30  
    31  	// Plan is the BuildpackPlan to resolve against.
    32  	Plan libcnb.BuildpackPlan
    33  }
    34  
    35  // MergeFunc takes two BuildpackPlanEntry's and returns a merged entry.
    36  type MergeFunc func(a, b libcnb.BuildpackPlanEntry) (libcnb.BuildpackPlanEntry, error)
    37  
    38  // ResolveWithMerge returns a single BuildpackPlanEntry that is a merged version of all entries that have a given name.
    39  // A merge function is used to describe how two entries are merged together.
    40  func (p *PlanEntryResolver) ResolveWithMerge(name string, f MergeFunc) (libcnb.BuildpackPlanEntry, bool, error) {
    41  	m := libcnb.BuildpackPlanEntry{}
    42  
    43  	var err error
    44  	for _, e := range p.Plan.Entries {
    45  		if e.Name == name {
    46  			if m, err = f(m, e); err != nil {
    47  				return libcnb.BuildpackPlanEntry{}, false, fmt.Errorf("error merging %+v and %+v: %w", m, e, err)
    48  			}
    49  		}
    50  	}
    51  
    52  	if reflect.DeepEqual(m, libcnb.BuildpackPlanEntry{}) {
    53  		return libcnb.BuildpackPlanEntry{}, false, nil
    54  	}
    55  
    56  	return m, true, nil
    57  }
    58  
    59  // ShallowMerge merges two BuildpackPlanEntry's together.  Declared versions are combined with a comma delimiter and
    60  // metadata is combined with the values for b taking priority over the values of a when the keys are duplicated.
    61  func ShallowMerge(a, b libcnb.BuildpackPlanEntry) (libcnb.BuildpackPlanEntry, error) {
    62  	var v []string
    63  	if a.Version != "" {
    64  		v = append(v, a.Version)
    65  	}
    66  	if b.Version != "" {
    67  		v = append(v, b.Version)
    68  	}
    69  
    70  	if err := mergo.Merge(&b, a); err != nil {
    71  		return libcnb.BuildpackPlanEntry{}, fmt.Errorf("unable to merge %+v and %+v: %w", a, b, err)
    72  	}
    73  
    74  	b.Version = strings.Join(v, ",")
    75  	return b, nil
    76  }
    77  
    78  // Resolve calls ResolveWithMerge function passing in the ShallowMerge function as the merge strategy.
    79  func (p *PlanEntryResolver) Resolve(name string) (libcnb.BuildpackPlanEntry, bool, error) {
    80  	return p.ResolveWithMerge(name, ShallowMerge)
    81  }