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 }