go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/acls/util.go (about)

     1  // Copyright 2022 The LUCI Authors.
     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  package acls
    16  
    17  import (
    18  	"fmt"
    19  	"sort"
    20  	"strings"
    21  
    22  	"go.chromium.org/luci/cv/internal/changelist"
    23  )
    24  
    25  // CheckResult tells the result of an ACL check performed.
    26  type CheckResult map[*changelist.CL]string
    27  
    28  // OK returns true if the result indicates no failures. False, otherwise.
    29  func (res CheckResult) OK() bool {
    30  	return len(res) == 0
    31  }
    32  
    33  // Has tells whether CheckResult contains the provided CL.
    34  func (res CheckResult) Has(cl *changelist.CL) bool {
    35  	_, ok := res[cl]
    36  	return ok
    37  }
    38  
    39  // Failure returns a failure message for a given RunCL.
    40  //
    41  // Returns an empty string, if the result was ok.
    42  func (res CheckResult) Failure(cl *changelist.CL) string {
    43  	if res.OK() {
    44  		return ""
    45  	}
    46  	msg, ok := res[cl]
    47  	if !ok {
    48  		eids := make([]string, 0, len(res))
    49  		for cl := range res {
    50  			eids = append(eids, cl.ExternalID.MustURL())
    51  		}
    52  		sort.Strings(eids)
    53  
    54  		var sb strings.Builder
    55  		sb.WriteString(okButDueToOthers)
    56  		for _, eid := range eids {
    57  			sb.WriteString("\n  - ")
    58  			sb.WriteString(eid)
    59  		}
    60  		return sb.String()
    61  	}
    62  	return msg
    63  }
    64  
    65  // FailuresSummary returns a summary of all the failures reported.
    66  //
    67  // Returns an empty string, if the result was ok.
    68  func (res CheckResult) FailuresSummary() string {
    69  	if res.OK() {
    70  		return ""
    71  	}
    72  	msgs := make([]string, 0, len(res))
    73  	for cl, msg := range res {
    74  		msgs = append(msgs, fmt.Sprintf("* %s\n%s", cl.ExternalID.MustURL(), msg))
    75  	}
    76  	sort.Strings(msgs)
    77  
    78  	var sb strings.Builder
    79  	sb.WriteString(msgs[0])
    80  	for _, msg := range msgs[1:] {
    81  		sb.WriteString("\n\n")
    82  		sb.WriteString(msg)
    83  	}
    84  	return sb.String()
    85  }
    86  
    87  // evalResult represents the result of an evaluation function.
    88  //
    89  // reason is the reason explaining why ok == false.
    90  type evalResult struct {
    91  	ok     bool
    92  	reason string
    93  }
    94  
    95  var (
    96  	yes = evalResult{ok: true}
    97  	no  = evalResult{}
    98  )
    99  
   100  func noWithReason(r string) evalResult {
   101  	return evalResult{reason: r}
   102  }