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