github.com/wolfd/bazel-gazelle@v0.14.0/internal/rule/platform_strings.go (about)

     1  /* Copyright 2017 The Bazel 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  
     7     http://www.apache.org/licenses/LICENSE-2.0
     8  
     9  Unless required by applicable law or agreed to in writing, software
    10  distributed under the License is distributed on an "AS IS" BASIS,
    11  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    12  See the License for the specific language governing permissions and
    13  limitations under the License.
    14  */
    15  
    16  package rule
    17  
    18  import (
    19  	"sort"
    20  	"strings"
    21  )
    22  
    23  // PlatformStrings contains a set of strings associated with a buildable
    24  // target in a package. This is used to store source file names,
    25  // import paths, and flags.
    26  //
    27  // Strings are stored in four sets: generic strings, OS-specific strings,
    28  // arch-specific strings, and OS-and-arch-specific strings. A string may not
    29  // be duplicated within a list or across sets; however, a string may appear
    30  // in more than one list within a set (e.g., in "linux" and "windows" within
    31  // the OS set). Strings within each list should be sorted, though this may
    32  // not be relied upon.
    33  type PlatformStrings struct {
    34  	// Generic is a list of strings not specific to any platform.
    35  	Generic []string
    36  
    37  	// OS is a map from OS name (anything in KnownOSs) to
    38  	// OS-specific strings.
    39  	OS map[string][]string
    40  
    41  	// Arch is a map from architecture name (anything in KnownArchs) to
    42  	// architecture-specific strings.
    43  	Arch map[string][]string
    44  
    45  	// Platform is a map from platforms to OS and architecture-specific strings.
    46  	Platform map[Platform][]string
    47  }
    48  
    49  // HasExt returns whether this set contains a file with the given extension.
    50  func (ps *PlatformStrings) HasExt(ext string) bool {
    51  	return ps.firstExtFile(ext) != ""
    52  }
    53  
    54  func (ps *PlatformStrings) IsEmpty() bool {
    55  	return len(ps.Generic) == 0 && len(ps.OS) == 0 && len(ps.Arch) == 0 && len(ps.Platform) == 0
    56  }
    57  
    58  // Flat returns all the strings in the set, sorted and de-duplicated.
    59  func (ps *PlatformStrings) Flat() []string {
    60  	unique := make(map[string]struct{})
    61  	for _, s := range ps.Generic {
    62  		unique[s] = struct{}{}
    63  	}
    64  	for _, ss := range ps.OS {
    65  		for _, s := range ss {
    66  			unique[s] = struct{}{}
    67  		}
    68  	}
    69  	for _, ss := range ps.Arch {
    70  		for _, s := range ss {
    71  			unique[s] = struct{}{}
    72  		}
    73  	}
    74  	for _, ss := range ps.Platform {
    75  		for _, s := range ss {
    76  			unique[s] = struct{}{}
    77  		}
    78  	}
    79  	flat := make([]string, 0, len(unique))
    80  	for s := range unique {
    81  		flat = append(flat, s)
    82  	}
    83  	sort.Strings(flat)
    84  	return flat
    85  }
    86  
    87  func (ps *PlatformStrings) firstExtFile(ext string) string {
    88  	for _, f := range ps.Generic {
    89  		if strings.HasSuffix(f, ext) {
    90  			return f
    91  		}
    92  	}
    93  	for _, fs := range ps.OS {
    94  		for _, f := range fs {
    95  			if strings.HasSuffix(f, ext) {
    96  				return f
    97  			}
    98  		}
    99  	}
   100  	for _, fs := range ps.Arch {
   101  		for _, f := range fs {
   102  			if strings.HasSuffix(f, ext) {
   103  				return f
   104  			}
   105  		}
   106  	}
   107  	for _, fs := range ps.Platform {
   108  		for _, f := range fs {
   109  			if strings.HasSuffix(f, ext) {
   110  				return f
   111  			}
   112  		}
   113  	}
   114  	return ""
   115  }
   116  
   117  // Map applies a function that processes individual strings to the strings
   118  // in "ps" and returns a new PlatformStrings with the result. Empty strings
   119  // returned by the function are dropped.
   120  func (ps *PlatformStrings) Map(f func(s string) (string, error)) (PlatformStrings, []error) {
   121  	var errors []error
   122  	mapSlice := func(ss []string) ([]string, error) {
   123  		rs := make([]string, 0, len(ss))
   124  		for _, s := range ss {
   125  			if r, err := f(s); err != nil {
   126  				errors = append(errors, err)
   127  			} else if r != "" {
   128  				rs = append(rs, r)
   129  			}
   130  		}
   131  		return rs, nil
   132  	}
   133  	result, _ := ps.MapSlice(mapSlice)
   134  	return result, errors
   135  }
   136  
   137  // MapSlice applies a function that processes slices of strings to the strings
   138  // in "ps" and returns a new PlatformStrings with the results.
   139  func (ps *PlatformStrings) MapSlice(f func([]string) ([]string, error)) (PlatformStrings, []error) {
   140  	var errors []error
   141  
   142  	mapSlice := func(ss []string) []string {
   143  		rs, err := f(ss)
   144  		if err != nil {
   145  			errors = append(errors, err)
   146  			return nil
   147  		}
   148  		return rs
   149  	}
   150  
   151  	mapStringMap := func(m map[string][]string) map[string][]string {
   152  		if m == nil {
   153  			return nil
   154  		}
   155  		rm := make(map[string][]string)
   156  		for k, ss := range m {
   157  			ss = mapSlice(ss)
   158  			if len(ss) > 0 {
   159  				rm[k] = ss
   160  			}
   161  		}
   162  		if len(rm) == 0 {
   163  			return nil
   164  		}
   165  		return rm
   166  	}
   167  
   168  	mapPlatformMap := func(m map[Platform][]string) map[Platform][]string {
   169  		if m == nil {
   170  			return nil
   171  		}
   172  		rm := make(map[Platform][]string)
   173  		for k, ss := range m {
   174  			ss = mapSlice(ss)
   175  			if len(ss) > 0 {
   176  				rm[k] = ss
   177  			}
   178  		}
   179  		if len(rm) == 0 {
   180  			return nil
   181  		}
   182  		return rm
   183  	}
   184  
   185  	result := PlatformStrings{
   186  		Generic:  mapSlice(ps.Generic),
   187  		OS:       mapStringMap(ps.OS),
   188  		Arch:     mapStringMap(ps.Arch),
   189  		Platform: mapPlatformMap(ps.Platform),
   190  	}
   191  	return result, errors
   192  }