github.com/Zenithar/prototool@v1.3.0/internal/file/file.go (about)

     1  // Copyright (c) 2018 Uber Technologies, Inc.
     2  //
     3  // Permission is hereby granted, free of charge, to any person obtaining a copy
     4  // of this software and associated documentation files (the "Software"), to deal
     5  // in the Software without restriction, including without limitation the rights
     6  // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
     7  // copies of the Software, and to permit persons to whom the Software is
     8  // furnished to do so, subject to the following conditions:
     9  //
    10  // The above copyright notice and this permission notice shall be included in
    11  // all copies or substantial portions of the Software.
    12  //
    13  // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
    14  // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
    15  // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
    16  // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
    17  // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
    18  // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
    19  // THE SOFTWARE.
    20  
    21  package file
    22  
    23  import (
    24  	"fmt"
    25  	"path/filepath"
    26  	"time"
    27  
    28  	"github.com/uber/prototool/internal/settings"
    29  	"go.uber.org/zap"
    30  )
    31  
    32  // DefaultWalkTimeout is the default walk timeout.
    33  const DefaultWalkTimeout time.Duration = 3 * time.Second
    34  
    35  // ProtoSet represents a set of .proto files and an associated config.
    36  //
    37  // ProtoSets will be validated if returned from this package.
    38  type ProtoSet struct {
    39  	// The working directory path.
    40  	// Must be absolute.
    41  	// Must be cleaned.
    42  	WorkDirPath string
    43  	// The given directory path.
    44  	// This will be the same as WorkDirPath if files were given.
    45  	// Must be absolute.
    46  	// Must be cleaned.
    47  	DirPath string
    48  	// The directory path to slice of .proto files.
    49  	// All paths must be absolute.
    50  	// Must be cleaned.
    51  	DirPathToFiles map[string][]*ProtoFile
    52  	// The associated Config.
    53  	// Must be valid.
    54  	// The DirPath on the config may differ from the DirPath
    55  	// on the ProtoSet.
    56  	Config settings.Config
    57  }
    58  
    59  // ProtoFile represents a .proto file.
    60  type ProtoFile struct {
    61  	// The path to the .proto file.
    62  	// Must be absolute.
    63  	// Must be cleaned.
    64  	Path string
    65  	// The path to display in output.
    66  	// This will be relative to the working directory, or the absolute path
    67  	// if the file was outside the working directory.
    68  	DisplayPath string
    69  }
    70  
    71  // ProtoSetProvider provides ProtoSets.
    72  type ProtoSetProvider interface {
    73  	// GetMultipleForDir gets the ProtoSets for the given dirPath.
    74  	// Each ProtoSet will have the config assocated with all files associated with
    75  	// the ProtoSet.
    76  	//
    77  	// This will return all .proto files in the directory of the associated config file
    78  	// and all it's subdirectories, or the given directory and its subdirectories
    79  	// if there is no config file.
    80  	//
    81  	// Configs will be searched for starting at the directory of each .proto file
    82  	// and going up a directory until hitting root.
    83  	GetMultipleForDir(workDirPath string, dirPath string) ([]*ProtoSet, error)
    84  
    85  	// GetMultipleForFiles gets the ProtoSets for the given filePaths.
    86  	// Each ProtoSet will have the config assocated with all files associated with
    87  	// the ProtoSet.
    88  	//
    89  	// Configs will be searched for starting at the directory of each .proto file
    90  	// and going up a directory until hitting root.
    91  	//
    92  	// This ignores excludes, all files given will be included.
    93  	GetMultipleForFiles(workDirPath string, filePaths ...string) ([]*ProtoSet, error)
    94  
    95  	// GetForDir does the same logic as GetMultipleForDir, but returns an error if there
    96  	// is not exactly one ProtoSet. We keep the original logic and testing for multiple
    97  	// ProtoSets around as we are still discussing this pre-v1.0.
    98  	// https://github.com/uber/prototool/issues/10
    99  	// https://github.com/uber/prototool/issues/93
   100  	GetForDir(workDirPath string, dirPath string) (*ProtoSet, error)
   101  
   102  	// GetForFiles does the same logic as GetMultipleForFiles, but returns an error if there
   103  	// is not exactly one ProtoSet. We keep the original logic and testing for multiple
   104  	// ProtoSets around as we are still discussing this pre-v1.0.
   105  	// https://github.com/uber/prototool/issues/10
   106  	// https://github.com/uber/prototool/issues/93
   107  	GetForFiles(workDirPath string, filePaths ...string) (*ProtoSet, error)
   108  }
   109  
   110  // ProtoSetProviderOption is an option for a new ProtoSetProvider.
   111  type ProtoSetProviderOption func(*protoSetProvider)
   112  
   113  // ProtoSetProviderWithLogger returns a ProtoSetProviderOption that uses the given logger.
   114  //
   115  // The default is to use zap.NewNop().
   116  func ProtoSetProviderWithLogger(logger *zap.Logger) ProtoSetProviderOption {
   117  	return func(protoSetProvider *protoSetProvider) {
   118  		protoSetProvider.logger = logger
   119  	}
   120  }
   121  
   122  // ProtoSetProviderWithConfigData returns a ProtoSetProviderOption that uses the given configuration
   123  // data instead of using configuration files that are found. This acts as if there is only one
   124  // configuration file at the current working directory. All found configuration files are ignored.
   125  func ProtoSetProviderWithConfigData(configData string) ProtoSetProviderOption {
   126  	return func(protoSetProvider *protoSetProvider) {
   127  		protoSetProvider.configData = configData
   128  	}
   129  }
   130  
   131  // ProtoSetProviderWithWalkTimeout returns a ProtoSetProviderOption will timeout after walking
   132  // a directory structure when searching for Protobuf files after the given amount of time.
   133  //
   134  // The default is to timeout after DefaultTimeoutDuration.
   135  // Set to 0 for no timeout.
   136  func ProtoSetProviderWithWalkTimeout(walkTimeout time.Duration) ProtoSetProviderOption {
   137  	return func(protoSetProvider *protoSetProvider) {
   138  		protoSetProvider.walkTimeout = walkTimeout
   139  	}
   140  }
   141  
   142  // NewProtoSetProvider returns a new ProtoSetProvider.
   143  func NewProtoSetProvider(options ...ProtoSetProviderOption) ProtoSetProvider {
   144  	return newProtoSetProvider(options...)
   145  }
   146  
   147  // AbsClean returns the cleaned absolute path of the given path.
   148  func AbsClean(path string) (string, error) {
   149  	if path == "" {
   150  		return path, nil
   151  	}
   152  	if !filepath.IsAbs(path) {
   153  		return filepath.Abs(path)
   154  	}
   155  	return filepath.Clean(path), nil
   156  }
   157  
   158  // CheckAbs is a convenience functions for determining
   159  // whether a path is an absolute path.
   160  func CheckAbs(path string) error {
   161  	if !filepath.IsAbs(path) {
   162  		return fmt.Errorf("expected absolute path but was %s", path)
   163  	}
   164  	return nil
   165  }