github.com/paketo-buildpacks/libpak@v1.70.0/internal/toml_writer.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 internal
    18  
    19  import (
    20  	"fmt"
    21  	"os"
    22  	"path/filepath"
    23  	"sort"
    24  	"strings"
    25  
    26  	"github.com/BurntSushi/toml"
    27  	"github.com/buildpacks/libcnb"
    28  	"github.com/heroku/color"
    29  
    30  	"github.com/paketo-buildpacks/libpak/bard"
    31  )
    32  
    33  // TOMLWriter is an implementation of the libcnb.TOMLWriter interface.
    34  type TOMLWriter struct {
    35  	logger bard.Logger
    36  }
    37  
    38  // TOMLWriterOption is a function for configuring a TOMLWriter instance.
    39  type TOMLWriterOption func(writer TOMLWriter) TOMLWriter
    40  
    41  // WithTOMLWriterLogger creates an TOMLWriterOption that configures the logger.
    42  func WithTOMLWriterLogger(logger bard.Logger) TOMLWriterOption {
    43  	return func(writer TOMLWriter) TOMLWriter {
    44  		writer.logger = logger
    45  		return writer
    46  	}
    47  }
    48  
    49  // NewTOMLWriter creates a new instance that writes to the filesystem and writes to the default bard.Logger.
    50  func NewTOMLWriter(options ...TOMLWriterOption) TOMLWriter {
    51  	w := TOMLWriter{
    52  		logger: bard.NewLogger(os.Stdout),
    53  	}
    54  
    55  	for _, option := range options {
    56  		w = option(w)
    57  	}
    58  
    59  	return w
    60  }
    61  
    62  // Write creates the path's parent directories, and creates a new file or truncates an existing file and then marshals
    63  // the value to the file.
    64  func (t TOMLWriter) Write(path string, value interface{}) error {
    65  	if value == nil {
    66  		return nil
    67  	}
    68  
    69  	switch v := value.(type) {
    70  	case libcnb.LaunchTOML:
    71  		if len(v.Slices) > 0 {
    72  			t.logger.Headerf("%d application slices", len(v.Slices))
    73  		}
    74  
    75  		if len(v.Labels) > 0 {
    76  			t.logger.Header("Image labels:")
    77  
    78  			sort.Slice(v.Labels, func(i, j int) bool {
    79  				return v.Labels[i].Key < v.Labels[j].Key
    80  			})
    81  
    82  			for _, l := range v.Labels {
    83  				t.logger.Headerf("  %s", l.Key)
    84  			}
    85  		}
    86  
    87  		if len(v.Processes) > 0 {
    88  			t.logger.Header("Process types:")
    89  
    90  			sort.Slice(v.Processes, func(i int, j int) bool {
    91  				return v.Processes[i].Type < v.Processes[j].Type
    92  			})
    93  
    94  			max := t.maxTypeLength(v.Processes)
    95  			for _, p := range v.Processes {
    96  				sb := strings.Builder{}
    97  				sb.WriteString(fmt.Sprintf("  %s: ", color.CyanString(p.Type)))
    98  
    99  				for i := 0; i < max-len(p.Type); i++ {
   100  					sb.WriteString(" ")
   101  				}
   102  
   103  				sb.WriteString(p.Command)
   104  
   105  				for _, a := range p.Arguments {
   106  					sb.WriteString(fmt.Sprintf(" %s", a))
   107  				}
   108  
   109  				if p.Direct {
   110  					sb.WriteString(" (direct)")
   111  				}
   112  
   113  				t.logger.Header(sb.String())
   114  			}
   115  		}
   116  	case libcnb.Store:
   117  		if len(v.Metadata) > 0 {
   118  			t.logger.Header("Persistent metadata:")
   119  
   120  			var names []string
   121  			for k := range v.Metadata {
   122  				names = append(names, k)
   123  			}
   124  
   125  			sort.Strings(names)
   126  
   127  			for _, n := range names {
   128  				t.logger.Headerf("  %s", n)
   129  			}
   130  		}
   131  	}
   132  
   133  	d := filepath.Dir(path)
   134  	if err := os.MkdirAll(d, 0755); err != nil {
   135  		return fmt.Errorf("unable to mkdir %s\n%w", d, err)
   136  	}
   137  
   138  	file, err := os.OpenFile(path, os.O_CREATE|os.O_TRUNC|os.O_WRONLY, 0644)
   139  	if err != nil {
   140  		return fmt.Errorf("unable to open file %s\n%w", path, err)
   141  	}
   142  	defer file.Close()
   143  
   144  	return toml.NewEncoder(file).Encode(value)
   145  }
   146  
   147  func (TOMLWriter) maxTypeLength(processes []libcnb.Process) int {
   148  	max := 0
   149  
   150  	for _, p := range processes {
   151  		if l := len(p.Type); l > max {
   152  			max = l
   153  		}
   154  	}
   155  
   156  	return max
   157  }