go.chromium.org/luci@v0.0.0-20240309015107-7cdc2e660f33/cv/internal/gerrit/util.go (about) 1 // Copyright 2021 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 gerrit 16 17 import ( 18 "errors" 19 "fmt" 20 "strings" 21 "unicode/utf8" 22 ) 23 24 // MaxMessageLength is the max message length for Gerrit as of Jun 2020 25 // based on error messages. 26 const MaxMessageLength = 16384 27 28 // PlaceHolder is added to the message when the length of the message 29 // exceeds `MaxMessageLength` and human message gets truncated. 30 const PlaceHolder = "\n...[truncated too long message]" 31 32 // TruncateMessage truncates the message and appends `PlaceHolder` so that 33 // the string doesn't exceed `MaxMessageLength`. 34 // 35 // If the input message is a valid utf-8 string, the result string will also 36 // be a valid utf-8 string 37 func TruncateMessage(msg string) string { 38 return truncate(msg, MaxMessageLength) 39 } 40 41 func truncate(msg string, maxLen int) string { 42 if len(msg) <= maxLen { 43 return msg 44 } 45 ret := msg[:maxLen-len(PlaceHolder)] 46 if utf8.ValidString(msg) { 47 end := len(ret) 48 start := end - 1 49 for ; start >= 0; start-- { 50 if !utf8.RuneStart(ret[start]) { 51 continue 52 } 53 if r, size := utf8.DecodeRuneInString(ret[start:end]); r != utf8.RuneError { 54 ret = ret[:start+size] 55 break 56 } 57 // non-first bytes for utf-8 encoding always have the top two bits 58 // set to 10. So a valid start bytes will never be in the middle of 59 // a valid utf-8 character. 60 end = start 61 } 62 } 63 return ret + PlaceHolder 64 } 65 66 // Tag constructs a CV specific Gerrit Tag for SetReview request. 67 // 68 // See: https://gerrit-review.googlesource.com/Documentation/rest-api-changes.html#review-input 69 // Panics if name is not provided or name contains "~". suffix is optional. 70 func Tag(name string, suffix string) string { 71 switch { 72 case name == "": 73 panic(errors.New("tag name MUST NOT be empty")) 74 case strings.Contains(name, "~"): 75 panic(errors.New("tag name MUST NOT contain `~`")) 76 case suffix != "": 77 return fmt.Sprintf("autogenerated:luci-cv:%s~%s", name, suffix) 78 default: 79 return fmt.Sprintf("autogenerated:luci-cv:%s", name) 80 } 81 }