github.com/choria-io/go-choria@v0.28.1-0.20240416190746-b3bf9c7d5a45/filter/classes/classes.go (about)

     1  // Copyright (c) 2019-2022, R.I. Pienaar and the Choria Project contributors
     2  //
     3  // SPDX-License-Identifier: Apache-2.0
     4  
     5  package classes
     6  
     7  import (
     8  	"bufio"
     9  	"fmt"
    10  	"os"
    11  	"regexp"
    12  	"sort"
    13  	"strings"
    14  )
    15  
    16  // Logger provides logging facilities
    17  type Logger interface {
    18  	Warnf(format string, args ...any)
    19  }
    20  
    21  func Match(needles []string, classes []string) bool {
    22  	matched := 0
    23  	failed := 0
    24  
    25  	for _, needle := range needles {
    26  		if strings.HasPrefix(needle, "/") && strings.HasSuffix(needle, "/") {
    27  			needle = strings.TrimPrefix(needle, "/")
    28  			needle = strings.TrimSuffix(needle, "/")
    29  
    30  			needle = fmt.Sprintf("(?i)%s", needle)
    31  
    32  			if hasClassMatching(needle, classes) {
    33  				matched++
    34  			} else {
    35  				failed++
    36  			}
    37  
    38  			continue
    39  		}
    40  
    41  		if hasClass(needle, classes) {
    42  			matched++
    43  		} else {
    44  			failed++
    45  		}
    46  	}
    47  
    48  	return failed == 0 && matched > 0
    49  }
    50  
    51  // MatchFile classes on a AND basis
    52  func MatchFile(needles []string, source string, log Logger) bool {
    53  	classes, err := ReadClasses(source)
    54  	if err != nil {
    55  		log.Warnf("Could not parse classes file %s: %s", source, err)
    56  		return false
    57  	}
    58  
    59  	return Match(needles, classes)
    60  }
    61  
    62  func hasClassMatching(needle string, stack []string) bool {
    63  	for _, class := range stack {
    64  		if match, _ := regexp.MatchString(needle, class); match {
    65  			return true
    66  		}
    67  	}
    68  
    69  	return false
    70  }
    71  
    72  func hasClass(needle string, stack []string) bool {
    73  	lneedle := strings.ToLower(needle)
    74  	for _, class := range stack {
    75  		if class == lneedle {
    76  			return true
    77  		}
    78  	}
    79  
    80  	return false
    81  }
    82  
    83  // ReadClasses reads a given file and attempts to parse it as a typical classes file
    84  func ReadClasses(file string) ([]string, error) {
    85  	classes := []string{}
    86  
    87  	fh, err := os.Open(file)
    88  	if err != nil {
    89  		return classes, err
    90  	}
    91  
    92  	defer fh.Close()
    93  
    94  	scanner := bufio.NewScanner(fh)
    95  	for scanner.Scan() {
    96  		classes = append(classes, scanner.Text())
    97  	}
    98  
    99  	sort.Strings(classes)
   100  
   101  	return classes, nil
   102  }