github.com/SuCicada/su-hugo@v1.0.0/hugofs/glob/filename_filter.go (about)

     1  // Copyright 2021 The Hugo Authors. All rights reserved.
     2  //
     3  // Licensed under the Apache License, Version 2.0 (the "License");
     4  // you may not use this file except in compliance with the License.
     5  // You may obtain a copy of the License at
     6  // http://www.apache.org/licenses/LICENSE-2.0
     7  //
     8  // Unless required by applicable law or agreed to in writing, software
     9  // distributed under the License is distributed on an "AS IS" BASIS,
    10  // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    11  // See the License for the specific language governing permissions and
    12  // limitations under the License.
    13  
    14  package glob
    15  
    16  import (
    17  	"path"
    18  	"path/filepath"
    19  	"strings"
    20  
    21  	"github.com/gobwas/glob"
    22  )
    23  
    24  type FilenameFilter struct {
    25  	shouldInclude func(filename string) bool
    26  	inclusions    []glob.Glob
    27  	dirInclusions []glob.Glob
    28  	exclusions    []glob.Glob
    29  	isWindows     bool
    30  }
    31  
    32  func normalizeFilenameGlobPattern(s string) string {
    33  	// Use Unix separators even on Windows.
    34  	s = filepath.ToSlash(s)
    35  	if !strings.HasPrefix(s, "/") {
    36  		s = "/" + s
    37  	}
    38  	return s
    39  }
    40  
    41  // NewFilenameFilter creates a new Glob where the Match method will
    42  // return true if the file should be included.
    43  // Note that the inclusions will be checked first.
    44  func NewFilenameFilter(inclusions, exclusions []string) (*FilenameFilter, error) {
    45  	if inclusions == nil && exclusions == nil {
    46  		return nil, nil
    47  	}
    48  	filter := &FilenameFilter{isWindows: isWindows}
    49  
    50  	for _, include := range inclusions {
    51  		include = normalizeFilenameGlobPattern(include)
    52  		g, err := GetGlob(include)
    53  		if err != nil {
    54  			return nil, err
    55  		}
    56  		filter.inclusions = append(filter.inclusions, g)
    57  
    58  		// For mounts that do directory walking (e.g. content) we
    59  		// must make sure that all directories up to this inclusion also
    60  		// gets included.
    61  		dir := path.Dir(include)
    62  		parts := strings.Split(dir, "/")
    63  		for i := range parts {
    64  			pattern := "/" + filepath.Join(parts[:i+1]...)
    65  			g, err := GetGlob(pattern)
    66  			if err != nil {
    67  				return nil, err
    68  			}
    69  			filter.dirInclusions = append(filter.dirInclusions, g)
    70  		}
    71  	}
    72  
    73  	for _, exclude := range exclusions {
    74  		exclude = normalizeFilenameGlobPattern(exclude)
    75  		g, err := GetGlob(exclude)
    76  		if err != nil {
    77  			return nil, err
    78  		}
    79  		filter.exclusions = append(filter.exclusions, g)
    80  	}
    81  
    82  	return filter, nil
    83  }
    84  
    85  // MustNewFilenameFilter invokes NewFilenameFilter and panics on error.
    86  func MustNewFilenameFilter(inclusions, exclusions []string) *FilenameFilter {
    87  	filter, err := NewFilenameFilter(inclusions, exclusions)
    88  	if err != nil {
    89  		panic(err)
    90  	}
    91  	return filter
    92  }
    93  
    94  // NewFilenameFilterForInclusionFunc create a new filter using the provided inclusion func.
    95  func NewFilenameFilterForInclusionFunc(shouldInclude func(filename string) bool) *FilenameFilter {
    96  	return &FilenameFilter{shouldInclude: shouldInclude, isWindows: isWindows}
    97  }
    98  
    99  // Match returns whether filename should be included.
   100  func (f *FilenameFilter) Match(filename string, isDir bool) bool {
   101  	if f == nil {
   102  		return true
   103  	}
   104  	return f.doMatch(filename, isDir)
   105  	/*if f.shouldInclude == nil {
   106  		fmt.Printf("Match: %q (%t) => %t\n", filename, isDir, isMatch)
   107  	}
   108  	return isMatch*/
   109  }
   110  
   111  func (f *FilenameFilter) doMatch(filename string, isDir bool) bool {
   112  	if f == nil {
   113  		return true
   114  	}
   115  
   116  	if !strings.HasPrefix(filename, filepathSeparator) {
   117  		filename = filepathSeparator + filename
   118  	}
   119  
   120  	if f.shouldInclude != nil {
   121  		if f.shouldInclude(filename) {
   122  			return true
   123  		}
   124  		if f.isWindows {
   125  			// The Glob matchers below handles this by themselves,
   126  			// for the shouldInclude we need to take some extra steps
   127  			// to make this robust.
   128  			winFilename := filepath.FromSlash(filename)
   129  			if filename != winFilename {
   130  				if f.shouldInclude(winFilename) {
   131  					return true
   132  				}
   133  			}
   134  		}
   135  
   136  	}
   137  
   138  	for _, inclusion := range f.inclusions {
   139  		if inclusion.Match(filename) {
   140  			return true
   141  		}
   142  	}
   143  
   144  	if isDir && f.inclusions != nil {
   145  		for _, inclusion := range f.dirInclusions {
   146  			if inclusion.Match(filename) {
   147  				return true
   148  			}
   149  		}
   150  	}
   151  
   152  	for _, exclusion := range f.exclusions {
   153  		if exclusion.Match(filename) {
   154  			return false
   155  		}
   156  	}
   157  
   158  	return f.inclusions == nil && f.shouldInclude == nil
   159  }