github.com/TheSpiritXIII/controller-tools@v0.14.1/pkg/genall/output.go (about)

     1  /*
     2  Copyright 2019 The Kubernetes 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      http://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 genall
    18  
    19  import (
    20  	"fmt"
    21  	"io"
    22  	"os"
    23  	"path/filepath"
    24  
    25  	"github.com/TheSpiritXIII/controller-tools/pkg/loader"
    26  )
    27  
    28  // nopCloser is a WriteCloser whose Close
    29  // is a no-op.
    30  type nopCloser struct {
    31  	io.Writer
    32  }
    33  
    34  func (n nopCloser) Close() error {
    35  	return nil
    36  }
    37  
    38  // DirectoryPerGenerator produces output rules mapping output to a different subdirectory
    39  // of the given base directory for each generator (with each subdirectory specified as
    40  // the key in the input map).
    41  func DirectoryPerGenerator(base string, generators map[string]*Generator) OutputRules {
    42  	rules := OutputRules{
    43  		Default:     OutputArtifacts{Config: OutputToDirectory(base)},
    44  		ByGenerator: make(map[*Generator]OutputRule, len(generators)),
    45  	}
    46  
    47  	for name, gen := range generators {
    48  		rules.ByGenerator[gen] = OutputArtifacts{
    49  			Config: OutputToDirectory(filepath.Join(base, name)),
    50  		}
    51  	}
    52  
    53  	return rules
    54  }
    55  
    56  // OutputRules defines how to output artificats on a per-generator basis.
    57  type OutputRules struct {
    58  	// Default is the output rule used when no specific per-generator overrides match.
    59  	Default OutputRule
    60  	// ByGenerator contains specific per-generator overrides.
    61  	// NB(directxman12): this is a pointer to avoid issues if a given Generator becomes unhashable
    62  	// (interface values compare by "dereferencing" their internal pointer first, whereas pointers
    63  	// compare by the actual pointer itself).
    64  	ByGenerator map[*Generator]OutputRule
    65  }
    66  
    67  // ForGenerator returns the output rule that should be used
    68  // by the given Generator.
    69  func (o OutputRules) ForGenerator(gen *Generator) OutputRule {
    70  	if forGen, specific := o.ByGenerator[gen]; specific {
    71  		return forGen
    72  	}
    73  	return o.Default
    74  }
    75  
    76  // OutputRule defines how to output artifacts from a generator.
    77  type OutputRule interface {
    78  	// Open opens the given artifact path for writing.  If a package is passed,
    79  	// the artifact is considered to be used as part of the package (e.g.
    80  	// generated code), while a nil package indicates that the artifact is
    81  	// config (or something else not involved in Go compilation).
    82  	Open(pkg *loader.Package, path string) (io.WriteCloser, error)
    83  }
    84  
    85  // OutputToNothing skips outputting anything.
    86  var OutputToNothing = outputToNothing{}
    87  
    88  // +controllertools:marker:generateHelp:category=""
    89  
    90  // outputToNothing skips outputting anything.
    91  type outputToNothing struct{}
    92  
    93  func (o outputToNothing) Open(_ *loader.Package, _ string) (io.WriteCloser, error) {
    94  	return nopCloser{io.Discard}, nil
    95  }
    96  
    97  // +controllertools:marker:generateHelp:category=""
    98  
    99  // OutputToDirectory outputs each artifact to the given directory, regardless
   100  // of if it's package-associated or not.
   101  type OutputToDirectory string
   102  
   103  func (o OutputToDirectory) Open(_ *loader.Package, itemPath string) (io.WriteCloser, error) {
   104  	// ensure the directory exists
   105  	if err := os.MkdirAll(filepath.Dir(filepath.Join(string(o), itemPath)), os.ModePerm); err != nil {
   106  		return nil, err
   107  	}
   108  	path := filepath.Join(string(o), itemPath)
   109  	return os.Create(path)
   110  }
   111  
   112  // OutputToStdout outputs everything to standard-out, with no separation.
   113  //
   114  // Generally useful for single-artifact outputs.
   115  var OutputToStdout = outputToStdout{}
   116  
   117  // +controllertools:marker:generateHelp:category=""
   118  
   119  // outputToStdout outputs everything to standard-out, with no separation.
   120  //
   121  // Generally useful for single-artifact outputs.
   122  type outputToStdout struct{}
   123  
   124  func (o outputToStdout) Open(_ *loader.Package, _ string) (io.WriteCloser, error) {
   125  	return nopCloser{os.Stdout}, nil
   126  }
   127  
   128  // +controllertools:marker:generateHelp:category=""
   129  
   130  // OutputArtifacts outputs artifacts to different locations, depending on
   131  // whether they're package-associated or not.
   132  //
   133  // Non-package associated artifacts
   134  // are output to the Config directory, while package-associated ones are output
   135  // to their package's source files' directory, unless an alternate path is
   136  // specified in Code.
   137  type OutputArtifacts struct {
   138  	// Config points to the directory to which to write configuration.
   139  	Config OutputToDirectory
   140  	// Code overrides the directory in which to write new code (defaults to where the existing code lives).
   141  	Code OutputToDirectory `marker:",optional"`
   142  }
   143  
   144  func (o OutputArtifacts) Open(pkg *loader.Package, itemPath string) (io.WriteCloser, error) {
   145  	if pkg == nil {
   146  		return o.Config.Open(pkg, itemPath)
   147  	}
   148  
   149  	if o.Code != "" {
   150  		return o.Code.Open(pkg, itemPath)
   151  	}
   152  
   153  	if len(pkg.CompiledGoFiles) == 0 {
   154  		return nil, fmt.Errorf("cannot output to a package with no path on disk")
   155  	}
   156  	outDir := filepath.Dir(pkg.CompiledGoFiles[0])
   157  	outPath := filepath.Join(outDir, itemPath)
   158  	return os.Create(outPath)
   159  }